+ $supp_id = '';
+ if (strpos($atts, 'class=') === false) {
+ $atts .= ' class="footnote"';
+ }
+
+ if (strpos($atts, ' id=') === false) {
+ $atts .= ' id="fn' . $fnid . '"';
+ } else {
+ $supp_id = ' id="fn' . $fnid . '"';
+ }
+
+ if (strpos($att, '^') === false) {
+ $sup = $this->formatFootnote($fns['fnid'], $supp_id);
+ } else {
+ $sup = $this->formatFootnote(''.$fns['fnid'] .' ', $supp_id);
+ }
+
+ $content = $sup . ' ' . $content;
+ }
+
+ if ($tag == "bq") {
+ $cite = $this->shelveURL($cite);
+ $cite = ($cite != '') ? ' cite="' . $cite . '"' : '';
+ $o1 = "\n";
+ $o2 = "\tparseAttribs($att, '', 0).">";
+ $c2 = "
";
+ $c1 = "\n ";
+ } elseif ($tag == 'bc') {
+ $o1 = "";
+ $c1 = "
";
+ $content = $this->shelve($this->rEncodeHTML($content));
+ } elseif ($tag == 'notextile') {
+ $content = $this->shelve($content);
+ $o1 = '';
+ $o2 = '';
+ $c1 = '';
+ $c2 = '';
+ } elseif ($tag == 'pre') {
+ $content = $this->shelve($this->rEncodeHTML($content));
+ $o1 = "";
+ $o2 = '';
+ $c2 = '';
+ $c1 = " ";
+ } elseif ($tag == '###') {
+ $eat = true;
+ } else {
+ $o2 = "<$tag$atts>";
+ $c2 = "$tag>";
+ }
+
+ $content = (!$eat) ? $this->graf($content) : '';
+
+ return array($o1, $o2, $content, $c2, $c1, $eat);
+ }
+
+ /**
+ * Formats a footnote.
+ *
+ * @param string $marker The marker
+ * @param string $atts Attributes
+ * @param bool $anchor TRUE, if its a reference link
+ * @return string Processed footnote
+ */
+
+ protected function formatFootnote($marker, $atts = '', $anchor = true)
+ {
+ $pattern = ($anchor) ? $this->symbols['fn_foot_pattern'] : $this->symbols['fn_ref_pattern'];
+ return $this->replaceMarkers($pattern, array('atts' => $atts, 'marker' => $marker));
+ }
+
+ /**
+ * Replaces markers with replacements in the given input.
+ *
+ * @param string $text The input
+ * @param array $replacements Marker replacement pairs
+ * @return string
+ */
+
+ protected function replaceMarkers($text, $replacements)
+ {
+ if (!empty($replacements)) {
+ foreach ($replacements as $k => $r) {
+ $text = str_replace('{'.$k.'}', $r, $text);
+ }
+ }
+ return $text;
+ }
+
+ /**
+ * Parses HTML comments in the given input.
+ *
+ * This method finds HTML comments in the given input
+ * and replaces them with reference tokens.
+ *
+ * @param string $text Textile input
+ * @return string $text Processed input
+ */
+
+ protected function getHTMLComments($text)
+ {
+ $text = preg_replace_callback(
+ "/\/sx",
+ array(&$this, "fParseHTMLComments"),
+ $text
+ );
+ return $text;
+ }
+
+ /**
+ * Formats a HTML comment.
+ *
+ * Stores the comment on the shelf and returns
+ * a reference token wrapped in to a HTML comment.
+ *
+ * @param array $m Options
+ * @return string Reference token wrapped to a HTML comment tags
+ */
+
+ protected function fParseHTMLComments($m)
+ {
+ return '';
+ }
+
+ /**
+ * Parses paragraphs in the given input.
+ *
+ * @param string $text Textile input
+ * @return string Processed input
+ */
+
+ protected function graf($text)
+ {
+ // Handle normal paragraph text
+ if (!$this->lite) {
+ // Notextile blocks and inlines
+ $text = $this->noTextile($text);
+ // Handle code
+ $text = $this->code($text);
+ }
+
+ // HTML comments --
+ $text = $this->getHTMLComments($text);
+ // Consume link aliases
+ $text = $this->getRefs($text);
+ // Treat quoted quote as a special glyph.
+ $text = $this->glyphQuotedQuote($text);
+ // Generate links
+ $text = $this->links($text);
+
+ // Handle images (if permitted)
+ if (!$this->noimage) {
+ $text = $this->images($text);
+ }
+
+ if (!$this->lite) {
+ // Handle tables
+ $text = $this->tables($text);
+ // Handle redcloth-style definition lists
+ $text = $this->redclothLists($text);
+ // Handle ordered & unordered lists plus txp-style definition lists
+ $text = $this->textileLists($text);
+ }
+
+ // Inline markup (em, strong, sup, sub, del etc)
+ $text = $this->spans($text);
+
+ if (!$this->lite) {
+ // Turn footnote references into supers or links.
+ // As footnote blocks are banned in lite mode there is no point
+ // generating links for them.
+ $text = $this->footnoteRefs($text);
+
+ // Turn note references into links
+ $text = $this->noteRefs($text);
+ }
+
+ // Glyph level substitutions (mainly typographic -- " & ' => curly quotes, -- => em-dash etc.
+ $text = $this->glyphs($text);
+
+ return rtrim($text, "\n");
+ }
+
+ /**
+ * Replaces Textile span tags with their equivalent HTML inline tags.
+ *
+ * @param string $text The Textile document to perform the replacements in
+ * @return string The Textile document with spans replaced by their HTML inline equivalents
+ */
+
+ protected function spans($text)
+ {
+ $span_tags = array_keys($this->span_tags);
+ $pnct = ".,\"'?!;:‹›«»„“”‚‘’";
+ $this->span_depth++;
+
+ if ($this->span_depth <= $this->max_span_depth) {
+ foreach ($span_tags as $tag) {
+ $tag = preg_quote($tag);
+ $text = preg_replace_callback(
+ "/
+ (?P^|(?<=[\s>$pnct\(])|[{[])
+ (?P$tag)(?!$tag)
+ (?P{$this->cls})
+ (?!$tag)
+ (?::(?P\S+[^$tag]{$this->regex_snippets['space']}))?
+ (?P[^{$this->regex_snippets['space']}$tag]+|\S.*?[^\s$tag\n])
+ (?P[$pnct]*)
+ $tag
+ (?P$|[\[\]}<]|(?=[$pnct]{1,2}[^0-9]|\s|\)))
+ /x".$this->regex_snippets['mod'],
+ array(&$this, "fSpan"),
+ $text
+ );
+ }
+ }
+ $this->span_depth--;
+ return $text;
+ }
+
+ /**
+ * Formats a span tag and stores it on the shelf.
+ *
+ * @param array $m Options
+ * @return string Content wrapped to reference tokens
+ * @see Parser::spans()
+ */
+
+ protected function fSpan($m)
+ {
+ $tag = $this->span_tags[$m['tag']];
+ $atts = $this->parseAttribsToArray($m['atts']);
+
+ if ($m['cite'] != '') {
+ $atts['cite'] = trim($m['cite']);
+ ksort($atts);
+ }
+
+ $atts = $this->formatAttributeString($atts);
+ $content = $this->spans($m['content']);
+ $opentag = '<'.$tag.$atts.'>';
+ $closetag = ''.$tag.'>';
+ $tags = $this->storeTags($opentag, $closetag);
+ $out = "{$tags['open']}{$content}{$m['end']}{$tags['close']}";
+
+ if (($m['pre'] && !$m['tail']) || ($m['tail'] && !$m['pre'])) {
+ $out = $m['pre'].$out.$m['tail'];
+ }
+
+ return $out;
+ }
+
+ /**
+ * Stores a tag pair in the tag cache.
+ *
+ * @param string $opentag Opening tag
+ * @param string $closetag Closing tag
+ * @return array Reference tokens for both opening and closing tag
+ */
+
+ protected function storeTags($opentag, $closetag = '')
+ {
+ $tags = array();
+
+ $this->refCache[$this->refIndex] = $opentag;
+ $tags['open'] = $this->uid.$this->refIndex.':ospan ';
+ $this->refIndex++;
+
+ $this->refCache[$this->refIndex] = $closetag;
+ $tags['close'] = ' '.$this->uid.$this->refIndex.':cspan';
+ $this->refIndex++;
+
+ return $tags;
+ }
+
+ /**
+ * Replaces reference tokens with corresponding shelved span tags.
+ *
+ * This method puts all shelved span tags back to the final,
+ * parsed input.
+ *
+ * @param string $text The input
+ * @return string Processed text
+ * @see Parser::storeTags()
+ */
+
+ protected function retrieveTags($text)
+ {
+ $text = preg_replace_callback(
+ '/'.$this->uid.'(?P[0-9]+):ospan /',
+ array(&$this, 'fRetrieveTags'),
+ $text
+ );
+
+ $text = preg_replace_callback(
+ '/ '.$this->uid.'(?P[0-9]+):cspan/',
+ array(&$this, 'fRetrieveTags'),
+ $text
+ );
+
+ return $text;
+ }
+
+ /**
+ * Retrieves a tag from the tag cache.
+ *
+ * @param array $m Options
+ * @return string
+ * @see Parser::retrieveTags()
+ */
+
+ protected function fRetrieveTags($m)
+ {
+ return $this->refCache[$m['token']];
+ }
+
+ /**
+ * Parses note lists in the given input.
+ *
+ * This method should be ran after other blocks
+ * have been processed, but before reference tokens
+ * have been replaced with their replacements.
+ *
+ * @param string $text Textile input
+ * @return string Processed input
+ */
+
+ protected function placeNoteLists($text)
+ {
+ extract($this->regex_snippets);
+
+ // Sequence all referenced definitions...
+ if (!empty($this->notes)) {
+ $o = array();
+ foreach ($this->notes as $label => $info) {
+ if (!empty($info['seq'])) {
+ $o[$info['seq']] = $info;
+ $info['seq'] = $label;
+ } else {
+ $this->unreferencedNotes[] = $info; // Unreferenced definitions go here for possible future use.
+ }
+ }
+
+ if (!empty($o)) {
+ ksort($o);
+ }
+
+ $this->notes = $o;
+ }
+
+ // Replace list markers.
+ $text = preg_replace_callback(
+ "@notelist(?P{$this->c})".
+ "(?:\:(?P[$wrd|{$this->syms}]))?".
+ "(?P[\^!]?)(?P\+?)\.?$space*
@U$mod",
+ array(&$this, "fNoteLists"),
+ $text
+ );
+
+ return $text;
+ }
+
+ /**
+ * Formats a note list.
+ *
+ * @param array $m Options
+ * @return string Processed note list
+ */
+
+ protected function fNoteLists($m)
+ {
+ if (!$m['startchar']) {
+ $m['startchar'] = 'a';
+ }
+
+ $index = $m['links'].$m['extras'].$m['startchar'];
+
+ if (empty($this->notelist_cache[$index])) {
+ // If not in cache, build the entry...
+ $out = array();
+
+ if (!empty($this->notes)) {
+ foreach ($this->notes as $seq => $info) {
+ $links = $this->makeBackrefLink($info, $m['links'], $m['startchar']);
+ $atts = '';
+ if (!empty($info['def'])) {
+ $id = $info['id'];
+ extract($info['def']);
+ $out[] = "\t".''.$links.' '.$content.' ';
+ } else {
+ $out[] = "\t".''.$links.' Undefined Note [#'.$info['seq'].']. ';
+ }
+ }
+ }
+
+ if ('+' == $m['extras'] && !empty($this->unreferencedNotes)) {
+ foreach ($this->unreferencedNotes as $seq => $info) {
+ if (!empty($info['def'])) {
+ extract($info['def']);
+ $out[] = "\t".''.$content.' ';
+ }
+ }
+ }
+
+ $this->notelist_cache[$index] = join("\n", $out);
+ }
+
+ if ($this->notelist_cache[$index]) {
+ $atts = $this->parseAttribs($m['atts']);
+ return "\n{$this->notelist_cache[$index]}\n ";
+ }
+
+ return '';
+ }
+
+ /**
+ * Renders a note back reference link.
+ *
+ * This method renders an array of back reference
+ * links for notes.
+ *
+ * @param array $info Options
+ * @param string $g_links Reference type
+ * @param int $i Instance count
+ * @return string Processed input
+ */
+
+ protected function makeBackrefLink(&$info, $g_links, $i)
+ {
+ $link = '';
+ $atts = '';
+ $content = '';
+ $id = '';
+
+ if (!empty($info['def'])) {
+ extract($info['def']);
+ }
+
+ $backlink_type = ($link) ? $link : $g_links;
+ $allow_inc = (false === strpos($this->syms, $i));
+
+ $i_ = str_replace(array('&', ';', '#'), '', $this->encodeHigh($i));
+ $decode = (strlen($i) !== strlen($i_));
+
+ if ($backlink_type === '!') {
+ return '';
+ } elseif ($backlink_type === '^') {
+ return ''.$i.' ';
+ } else {
+ $out = array();
+
+ foreach ($info['refids'] as $id) {
+ $out[] = ''. (($decode) ? $this->decodeHigh($i_) : $i_) .' ';
+ if ($allow_inc) {
+ $i_++;
+ }
+ }
+
+ return join(' ', $out);
+ }
+ }
+
+ /**
+ * Formats note definitions.
+ *
+ * This method formats notes and stores them in
+ * note cache for later use and to build reference
+ * links.
+ *
+ * @param array $m Options
+ * @return string Empty string
+ */
+
+ protected function fParseNoteDefs($m)
+ {
+ $label = $m['label'];
+ $link = $m['link'];
+ $att = $m['att'];
+ $content = $m['content'];
+
+ // Assign an id if the note reference parse hasn't found the label yet.
+ if (empty($this->notes[$label]['id'])) {
+ $this->notes[$label]['id'] = $this->linkPrefix . ($this->linkIndex++);
+ }
+
+ // Ignores subsequent defs using the same label
+ if (empty($this->notes[$label]['def'])) {
+ $this->notes[$label]['def'] = array(
+ 'atts' => $this->parseAttribs($att),
+ 'content' => $this->graf($content),
+ 'link' => $link,
+ );
+ }
+ return '';
+ }
+
+ /**
+ * Parses note references in the given input.
+ *
+ * This method replaces note reference tags with
+ * links.
+ *
+ * @param string $text Textile input
+ * @return string
+ */
+
+ protected function noteRefs($text)
+ {
+ $text = preg_replace_callback(
+ "/\[(?P{$this->c})\#(?P[^\]!]+?)(?P[!]?)\]/Ux",
+ array(&$this, "fParseNoteRefs"),
+ $text
+ );
+ return $text;
+ }
+
+ /**
+ * Formats note reference links.
+ *
+ * By the time this function is called, all note lists will have been
+ * processed into the notes array, and we can resolve the link numbers in
+ * the order we process the references.
+ *
+ * @param array $m Options
+ * @return string Note reference
+ */
+
+ protected function fParseNoteRefs($m)
+ {
+ $atts = $this->parseAttribs($m['atts']);
+ $nolink = ($m['nolink'] === '!');
+
+ // Assign a sequence number to this reference if there isn't one already.
+
+ if (empty($this->notes[$m['label']]['seq'])) {
+ $num = $this->notes[$m['label']]['seq'] = ($this->note_index++);
+ } else {
+ $num = $this->notes[$m['label']]['seq'];
+ }
+
+ // Make our anchor point & stash it for possible use in backlinks when the
+ // note list is generated later.
+ $refid = $this->linkPrefix . ($this->linkIndex++);
+ $this->notes[$m['label']]['refids'][] = $refid;
+
+ // If we are referencing a note that hasn't had the definition parsed yet, then assign it an ID.
+
+ if (empty($this->notes[$m['label']]['id'])) {
+ $id = $this->notes[$m['label']]['id'] = $this->linkPrefix . ($this->linkIndex++);
+ } else {
+ $id = $this->notes[$m['label']]['id'];
+ }
+
+ // Build the link (if any).
+ $out = ''.$num.' ';
+
+ if (!$nolink) {
+ $out = ''.$out.' ';
+ }
+
+ // Build the reference.
+ return $this->replaceMarkers($this->symbols['nl_ref_pattern'], array('atts' => $atts, 'marker' => $out));
+ }
+
+ /**
+ * Parses URI into component parts.
+ *
+ * This method splits a URI-like string apart into component parts, while
+ * also providing validation.
+ *
+ * @param string $uri The string to pick apart (if possible)
+ * @param array $m Reference to an array the URI component parts are assigned to
+ * @return bool TRUE if the string validates as a URI
+ * @link http://tools.ietf.org/html/rfc3986#appendix-B
+ */
+
+ protected function parseURI($uri, &$m)
+ {
+ $r = "@^((?P[^:/?#]+):)?".
+ "(//(?P[^/?#]*))?".
+ "(?P[^?#]*)".
+ "(\?(?P[^#]*))?".
+ "(#(?P.*))?@";
+
+ $ok = preg_match($r, $uri, $m);
+ return $ok;
+ }
+
+ /**
+ * Checks whether a component part can be added to a URI.
+ *
+ * @param array $mask An array of allowed component parts
+ * @param string $name The component to add
+ * @param array $parts An array of existing components to modify
+ * @return bool TRUE if the component can be added
+ */
+
+ protected function addPart(&$mask, $name, &$parts)
+ {
+ return (in_array($name, $mask) && isset($parts[$name]) && '' !== $parts[$name]);
+ }
+
+ /**
+ * Rebuild a URI from parsed parts and a mask.
+ *
+ * @param array $parts Full array of URI parts
+ * @param string $mask Comma separated list of URI parts to include in the rebuilt URI
+ * @param bool $encode Flag to control encoding of the path part of the rebuilt URI
+ * @return string The rebuilt URI
+ * @link http://tools.ietf.org/html/rfc3986#section-5.3
+ */
+
+ protected function rebuildURI($parts, $mask = 'scheme,authority,path,query,fragment', $encode = true)
+ {
+ $mask = explode(',', $mask);
+ $out = '';
+
+ if ($this->addPart($mask, 'scheme', $parts)) {
+ $out .= $parts['scheme'] . ':';
+ }
+
+ if ($this->addPart($mask, 'authority', $parts)) {
+ $out .= '//' . $parts['authority'];
+ }
+
+ if ($this->addPart($mask, 'path', $parts)) {
+ if (!$encode) {
+ $out .= $parts['path'];
+ } else {
+ $pp = explode('/', $parts['path']);
+ foreach ($pp as &$p) {
+ $p = str_replace(array('%25', '%40'), array('%', '@'), rawurlencode($p));
+ if (!in_array($parts['scheme'], array('tel','mailto'))) {
+ $p = str_replace('%2B', '+', $p);
+ }
+ }
+
+ $pp = implode('/', $pp);
+ $out .= $pp;
+ }
+ }
+
+ if ($this->addPart($mask, 'query', $parts)) {
+ $out .= '?' . $parts['query'];
+ }
+
+ if ($this->addPart($mask, 'fragment', $parts)) {
+ $out .= '#' . $parts['fragment'];
+ }
+
+ return $out;
+ }
+
+ /**
+ * Parses and shelves links in the given input.
+ *
+ * This method parses the input Textile document for links.
+ * Formats and encodes them, and stores the created link
+ * elements in cache.
+ *
+ * @param string $text Textile input
+ * @return string The input document with link pulled out and replaced with tokens
+ */
+
+ protected function links($text)
+ {
+ $text = $this->markStartOfLinks($text);
+ return $this->replaceLinks($text);
+ }
+
+ /**
+ * Finds and marks the start of well formed links in the input text.
+ *
+ * @param string $text String to search for link starting positions
+ * @return string Text with links marked
+ * @see Parser::links()
+ */
+
+ protected function markStartOfLinks($text)
+ {
+ // Slice text on '":' boundaries. These always occur in inline
+ // links between the link text and the url part and are much more
+ // infrequent than '"' characters so we have less possible links
+ // to process.
+ $mod = $this->regex_snippets['mod'];
+ $slices = preg_split('/":(?='.$this->regex_snippets['char'].')/'.$mod, $text);
+
+ if (count($slices) > 1) {
+
+ // There are never any start of links in the last slice, so pop it
+ // off (we'll glue it back later).
+ $last_slice = array_pop($slices);
+
+ foreach ($slices as &$slice) {
+
+ // If there is no possible start quote then this slice is not a link
+ if (false === strpos($slice, '"')) {
+ continue;
+ }
+
+ // Cut this slice into possible starting points wherever we
+ // find a '"' character. Any of these parts could represent
+ // the start of the link text - we have to find which one.
+ $possible_start_quotes = explode('"', $slice);
+
+ // Start our search for the start of the link with the closest prior
+ // quote mark.
+ $possibility = rtrim(array_pop($possible_start_quotes));
+
+ // Init the balanced count. If this is still zero at the end
+ // of our do loop we'll mark the " that caused it to balance
+ // as the start of the link and move on to the next slice.
+ $balanced = 0;
+ $linkparts = array();
+ $iter = 0;
+
+ while (null !== $possibility) {
+ // Starting at the end, pop off the previous part of the
+ // slice's fragments.
+
+ // Add this part to those parts that make up the link text.
+ $linkparts[] = $possibility;
+ $len = strlen($possibility) > 0;
+
+ if ($len) {
+ // did this part inc or dec the balanced count?
+ if (preg_match('/^\S|=$/'.$mod, $possibility)) {
+ $balanced--;
+ }
+
+ if (preg_match('/\S$/'.$mod, $possibility)) {
+ $balanced++;
+ }
+
+ $possibility = array_pop($possible_start_quotes);
+ } else {
+ // If quotes occur next to each other, we get zero length strings.
+ // eg. ...""Open the door, HAL!"":url...
+ // In this case we count a zero length in the last position as a
+ // closing quote and others as opening quotes.
+ $balanced = (!$iter++) ? $balanced+1 : $balanced-1;
+
+ $possibility = array_pop($possible_start_quotes);
+
+ // If out of possible starting segments we back the last one
+ // from the linkparts array
+ if (null === $possibility) {
+ array_pop($linkparts);
+ break;
+ }
+
+ // If the next possibility is empty or ends in a space we have a
+ // closing ".
+ if (0 === strlen($possibility) ||
+ preg_match("~{$this->regex_snippets['space']}$~".$mod, $possibility)) {
+ $balanced = 0; // force search exit
+ }
+ }
+
+ if ($balanced <= 0) {
+ array_push($possible_start_quotes, $possibility);
+ break;
+ }
+
+ }
+
+ // Rebuild the link's text by reversing the parts and sticking them back
+ // together with quotes.
+ $link_content = implode('"', array_reverse($linkparts));
+
+ // Rebuild the remaining stuff that goes before the link but that's
+ // already in order.
+ $pre_link = implode('"', $possible_start_quotes);
+
+ // Re-assemble the link starts with a specific marker for the next regex.
+ $slice = $pre_link . $this->uid.'linkStartMarker:"' . $link_content;
+ }
+
+ // Add the last part back
+ $slices[] = $last_slice;
+ }
+
+ // Re-assemble the full text with the start and end markers
+ $text = implode('":', $slices);
+
+ return $text;
+ }
+
+ /**
+ * Replaces links with tokens and stores them on the shelf.
+ *
+ * @param string $text The input
+ * @return string Processed input
+ * @see Parser::links()
+ */
+
+ protected function replaceLinks($text)
+ {
+ $stopchars = "\s|^'\"*";
+
+ return preg_replace_callback(
+ '/
+ (?P\[)? # Optionally open with a square bracket eg. Look ["here":url]
+ '.$this->uid.'linkStartMarker:" # marks start of the link
+ (?P.*?) # grab the content of the inner "..." part of the link, can be anything but
+ # do not worry about matching class, id, lang or title yet
+ ": # literal ": marks end of atts + text + title block
+ (?P[^'.$stopchars.']*) # url upto a stopchar
+ /x'.$this->regex_snippets['mod'],
+ array(&$this, "fLink"),
+ $text
+ );
+ }
+
+ /**
+ * Formats a link and stores it on the shelf.
+ *
+ * @param array $m Options
+ * @return string Reference token for the shelved content
+ * @see Parser::replaceLinks()
+ */
+
+ protected function fLink($m)
+ {
+ $in = $m[0];
+ $pre = $m['pre'];
+ $inner = $m['inner'];
+ $url = $m['urlx'];
+ $m = array();
+
+ // Treat empty inner part as an invalid link.
+ $trimmed = trim($inner);
+ if (empty($trimmed)) {
+ return $pre.'"'.$inner.'":'.$url;
+ }
+
+ // Split inner into $atts, $text and $title..
+ preg_match(
+ '/
+ ^
+ (?P' . $this->cls . ') # $atts (if any)
+ ' . $this->regex_snippets['space'] . '* # any optional spaces
+ (?P # $text is...
+ (!.+!) # an image
+ | # else...
+ .+? # link text
+ ) # end of $text
+ (?:\((?P[^)]+?)\))? # $title (if any)
+ $
+ /x'.$this->regex_snippets['mod'],
+ $inner,
+ $m
+ );
+ $atts = isset($m['atts']) ? $m['atts'] : '';
+ $text = isset($m['text']) ? trim($m['text']) : $inner;
+ $title = isset($m['title']) ? $m['title'] : '';
+ $m = array();
+
+ $pop = $tight = '';
+ $url_chars = array();
+ $counts = array(
+ '[' => null,
+ ']' => substr_count($url, ']'), # We need to know how many closing square brackets we have
+ '(' => null,
+ ')' => null,
+ );
+
+ // Look for footnotes or other square-bracket delimieted stuff at the end of the url...
+ // eg. "text":url][otherstuff... will have "[otherstuff" popped back out.
+ // "text":url?q[]=x][123] will have "[123]" popped off the back, the remaining closing square brackets
+ // will later be tested for balance
+ if ($counts[']']) {
+ if (1 === preg_match('@(?P^.*\])(?P\[.*?)$@' . $this->regex_snippets['mod'], $url, $m)) {
+ $url = $m['url'];
+ $tight = $m['tight'];
+ $m = array();
+ }
+ }
+
+ // Split off any trailing text that isn't part of an array assignment.
+ // eg. "text":...?q[]=value1&q[]=value2 ... is ok
+ // "text":...?q[]=value1]following ... would have "following"
+ // popped back out and the remaining square bracket
+ // will later be tested for balance
+ if ($counts[']']) {
+ if (1 === preg_match('@(?P^.*\])(?!=)(?P.*?)$@' . $this->regex_snippets['mod'], $url, $m)) {
+ $url = $m['url'];
+ $tight = $m['end'] . $tight;
+ $m = array();
+ }
+ }
+
+ // Does this need to be mb_ enabled? We are only searching for text in the ASCII charset anyway
+ // Create an array of (possibly) multi-byte characters.
+ // This is going to allow us to pop off any non-matched or nonsense chars from the url
+ $len = strlen($url);
+ $url_chars = str_split($url);
+
+ // Now we have the array of all the multi-byte chars in the url we will parse the
+ // uri backwards and pop off
+ // any chars that don't belong there (like . or , or unmatched brackets of various kinds).
+ $first = true;
+ do {
+ $c = array_pop($url_chars);
+ $popped = false;
+ switch ($c) {
+
+ // Textile URL shouldn't end in these characters, we pop
+ // them off the end and push them out the back of the url again.
+ case '!':
+ case '?':
+ case ':':
+ case ';':
+ case '.':
+ case ',':
+ $pop = $c . $pop;
+ $popped = true;
+ break;
+
+ case '>':
+ $urlLeft = implode('', $url_chars);
+
+ if (preg_match('@(?P<\/[a-z]+)$@', $urlLeft, $m)) {
+ $url_chars = str_split(substr($urlLeft, 0, strlen($m['tag']) * -1));
+ $pop = $m['tag'] . $c . $pop;
+ $popped = true;
+ }
+
+ break;
+
+ case ']':
+ // If we find a closing square bracket we are going to see if it is balanced.
+ // If it is balanced with matching opening bracket then it is part of the URL
+ // else we spit it back out of the URL.
+ if (null === $counts['[']) {
+ $counts['['] = substr_count($url, '[');
+ }
+
+ if ($counts['['] === $counts[']']) {
+ // It is balanced, so keep it
+ $url_chars[] = $c;
+ } else {
+ // In the case of un-matched closing square brackets we just eat it
+ $popped = true;
+ $counts[']'] -= 1;
+ if ($first) {
+ $pre = '';
+ }
+ }
+ break;
+
+ case ')':
+ if (null === $counts[')']) {
+ $counts['('] = substr_count($url, '(');
+ $counts[')'] = substr_count($url, ')');
+ }
+
+ if ($counts['('] === $counts[')']) {
+ // It is balanced, so keep it
+ $url_chars[] = $c;
+ } else {
+ // Unbalanced so spit it out the back end
+ $pop = $c . $pop;
+ $counts[')'] -= 1;
+ $popped = true;
+ }
+ break;
+
+ default:
+ // We have an acceptable character for the end of the url so put it back and
+ // exit the character popping loop
+ $url_chars[] = $c;
+ break;
+ }
+ $first = false;
+ } while ($popped);
+
+ $url = implode('', $url_chars);
+ $uri_parts = array();
+ $this->parseURI($url, $uri_parts);
+
+ $scheme = $uri_parts['scheme'];
+ $scheme_in_list = in_array($scheme, $this->url_schemes);
+ $scheme_ok = ('' === $scheme) || $scheme_in_list;
+
+ if (!$scheme_ok) {
+ return str_replace($this->uid.'linkStartMarker:', '', $in);
+ }
+
+ if ('$' === $text) {
+ if ($scheme_in_list) {
+ $text = ltrim($this->rebuildURI($uri_parts, 'authority,path,query,fragment', false), '/');
+ } else {
+ if (isset($this->urlrefs[$url])) {
+ $url = urldecode($this->urlrefs[$url]);
+ }
+
+ $text = $url;
+ }
+ }
+
+ $text = trim($text);
+ $title = $this->encodeHTML($title);
+
+ if (!$this->noimage) {
+ $text = $this->images($text);
+ }
+
+ $text = $this->spans($text);
+ $text = $this->glyphs($text);
+ $url = $this->shelveURL($this->rebuildURI($uri_parts));
+ $a = $this->newTag(
+ 'a',
+ $this->parseAttribsToArray($atts),
+ false
+ )->title($title)->href($url, true)->rel($this->rel);
+ $tags = $this->storeTags((string) $a, '');
+ $out = $this->shelve($tags['open'].trim($text).$tags['close']);
+
+ return $pre . $out . $pop . $tight;
+ }
+
+ /**
+ * Finds URI aliases within the given input.
+ *
+ * This method finds URI aliases in the Textile input. Links are stored
+ * in an internal cache, so that they can be referenced from any link
+ * in the document.
+ *
+ * This operation happens before the actual link parsing takes place.
+ *
+ * @param string $text Textile input
+ * @return string The Textile document with any URI aliases removed
+ */
+
+ protected function getRefs($text)
+ {
+ $pattern = array();
+
+ foreach ($this->url_schemes as $scheme) {
+ $pattern[] = preg_quote($scheme.':', '/');
+ }
+
+ $pattern =
+ '/^\[(?P.+)\]'.
+ '(?P(?:'.join('|', $pattern).'|\/)\S+)'.
+ '(?='.$this->regex_snippets['space'].'|$)/Um';
+
+ return preg_replace_callback($pattern.$this->regex_snippets['mod'], array(&$this, "refs"), $text);
+ }
+
+ /**
+ * Parses, encodes and shelves the current URI alias.
+ *
+ * @param array $m Options
+ * @return string Empty string
+ * @see Parser::getRefs()
+ */
+
+ protected function refs($m)
+ {
+ $uri_parts = array();
+ $this->parseURI($m['url'], $uri_parts);
+ // Encodes URL if needed.
+ $this->urlrefs[$m['alias']] = ltrim($this->rebuildURI($uri_parts));
+ return '';
+ }
+
+ /**
+ * Shelves parsed URLs.
+ *
+ * Stores away a URL fragments that have been parsed
+ * and requires no more processing.
+ *
+ * @param string $text The URL
+ * @return string The fragment's unique reference ID
+ * @see Parser::retrieveURLs()
+ */
+
+ protected function shelveURL($text)
+ {
+ if ('' === $text) {
+ return '';
+ }
+
+ $this->refCache[$this->refIndex] = $text;
+ return $this->uid.($this->refIndex++).':url';
+ }
+
+ /**
+ * Replaces reference tokens with corresponding shelved URL.
+ *
+ * This method puts all shelved URLs back to the final,
+ * parsed input.
+ *
+ * @param string $text The input
+ * @return string Processed text
+ * @see Parser::shelveURL()
+ */
+
+ protected function retrieveURLs($text)
+ {
+ return preg_replace_callback('/'.$this->uid.'(?P[0-9]+):url/', array(&$this, 'retrieveURL'), $text);
+ }
+
+ /**
+ * Retrieves an URL from the shelve.
+ *
+ * @param array $m Options
+ * @return string The URL
+ */
+
+ protected function retrieveURL($m)
+ {
+ if (!isset($this->refCache[$m['token']])) {
+ return '';
+ }
+
+ $url = $this->refCache[$m['token']];
+ if (isset($this->urlrefs[$url])) {
+ $url = $this->urlrefs[$url];
+ }
+
+ return $this->rEncodeHTML($this->relURL($url));
+ }
+
+ /**
+ * Completes and formats a URL.
+ *
+ * @param string $url The URL
+ * @return string
+ */
+
+ protected function relURL($url)
+ {
+ $parts = @parse_url(urldecode($url));
+
+ if (empty($parts['scheme']) || $parts['scheme'] == 'http') {
+ if (empty($parts['host']) && (isset($parts['path']) && preg_match('/^\w/', $parts['path']))) {
+ $url = $this->relativeImagePrefix.$url;
+ }
+ }
+
+ return $url;
+ }
+
+ /**
+ * Checks if an URL is relative.
+ *
+ * The given URL is considered relative if it doesn't
+ * contain scheme and hostname.
+ *
+ * @param string $url The URL
+ * @return bool TRUE if relative, FALSE otherwise
+ */
+
+ protected function isRelURL($url)
+ {
+ $parts = @parse_url($url);
+ return (empty($parts['scheme']) && empty($parts['host']));
+ }
+
+ /**
+ * Parses and shelves images in the given input.
+ *
+ * This method parses the input Textile document for images and
+ * generates img HTML tags for each one found, caching the
+ * generated img tag internally and replacing the Textile image with a
+ * token to the cached tag.
+ *
+ * @param string $text Textile input
+ * @return string The input document with images pulled out and replaced with tokens
+ */
+
+ protected function images($text)
+ {
+ return preg_replace_callback(
+ '/
+ (?:[[{])? # pre
+ \! # opening !
+ (?P\<|\=|\>|<|>)? # optional alignment $algn
+ (?P'.$this->cls.') # optional style,class atts $atts
+ (?:\.\s)? # optional dot-space
+ (?P[^\s(!]+) # presume this is the src $url
+ \s? # optional space
+ (?:\((?P[^\)]+)\))? # optional title $title
+ \! # closing
+ (?::(?P\S+)(?regex_snippets['mod'],
+ array(&$this, "fImage"),
+ $text
+ );
+ }
+
+ /**
+ * Formats an image and stores it on the shelf.
+ *
+ * @param array $m Options
+ * @return string Reference token for the shelved content
+ * @see Parser::images()
+ */
+
+ protected function fImage($m)
+ {
+ $extras = '';
+
+ $align = (isset($m['align'])) ? $m['align'] : '';
+ $atts = $m['atts'];
+ $url = $m['url'];
+ $title = (isset($m['title'])) ? $m['title'] : '';
+ $href = (isset($m['href'])) ? $m['href'] : '';
+
+ $alignments = array(
+ '<' => 'left',
+ '=' => 'center',
+ '>' => 'right',
+ '<' => 'left',
+ '>' => 'right',
+ );
+
+ if (isset($alignments[$align])) {
+ if ('html5' === $this->doctype) {
+ $extras = 'align-'.$alignments[$align];
+ $align = '';
+ } else {
+ $align = $alignments[$align];
+ }
+ } else {
+ $align = '';
+ }
+
+ if ($title) {
+ $title = $this->encodeHTML($title);
+ }
+
+ $img = $this->newTag('img', $this->parseAttribsToArray($atts, '', 1, $extras))
+ ->align($align)
+ ->alt($title, true)
+ ->src($this->shelveURL($url), true)
+ ->title($title);
+
+ if (!$this->dimensionless_images && $this->isRelUrl($url)) {
+ $real_location = realpath($this->doc_root.ltrim($url, $this->ds));
+
+ if ($real_location) {
+ if ($size = getimagesize($real_location)) {
+ $img->height($size[1])->width($size[0]);
+ }
+ }
+ }
+
+ $out = (string) $img;
+
+ if ($href) {
+ $href = $this->shelveURL($href);
+ $link = $this->newTag('a', array(), false)->href($href)->rel($this->rel);
+ $out = (string) $link . "$img";
+ }
+
+ return $this->shelve($out);
+ }
+
+ /**
+ * Parses code blocks in the given input.
+ *
+ * @param string $text The input
+ * @return string Processed text
+ */
+
+ protected function code($text)
+ {
+ $text = $this->doSpecial($text, '', '
', 'fCode');
+ $text = $this->doSpecial($text, '@', '@', 'fCode');
+ $text = $this->doSpecial($text, '', ' ', 'fPre');
+ return $text;
+ }
+
+ /**
+ * Formats inline code tags.
+ *
+ * @param array $m
+ * @return string
+ */
+
+ protected function fCode($m)
+ {
+ return $m['before'].$this->shelve(''.$this->rEncodeHTML($m['content']).'
');
+ }
+
+ /**
+ * Formats pre tags.
+ *
+ * @param array $m Options
+ * @return string
+ */
+
+ protected function fPre($m)
+ {
+ return $m['before'].''.$this->shelve($this->rEncodeHTML($m['content'])).' ';
+ }
+
+ /**
+ * Shelves parsed content.
+ *
+ * Stores away a fragment of the source text that have been parsed
+ * and requires no more processing.
+ *
+ * @param string $val The content
+ * @return string The fragment's unique reference ID
+ * @see Parser::retrieve()
+ */
+
+ protected function shelve($val)
+ {
+ $i = $this->uid.($this->refIndex++).':shelve';
+ $this->shelf[$i] = $val;
+ return $i;
+ }
+
+ /**
+ * Replaces reference tokens with corresponding shelved content.
+ *
+ * This method puts all shelved content back to the final,
+ * parsed input.
+ *
+ * @param string $text The input
+ * @return string Processed text
+ * @see Parser::shelve()
+ */
+
+ protected function retrieve($text)
+ {
+ if ($this->shelf) {
+ do {
+ $old = $text;
+ $text = str_replace(array_keys($this->shelf), $this->shelf, $text);
+ } while ($text != $old);
+ }
+
+ return $text;
+ }
+
+ /**
+ * Removes BOM and unifies line ending in the given input.
+ *
+ * @param string $text Input Textile
+ * @return string Cleaned version of the input
+ */
+
+ protected function cleanWhiteSpace($text)
+ {
+ // Removes byte order mark.
+ $out = preg_replace("/^\xEF\xBB\xBF|\x1A/", '', $text);
+ // Replaces CRLF and CR with single LF.
+ $out = preg_replace("/\r\n?/", "\n", $out);
+ // Removes leading tabs and spaces, if the line is otherwise empty.
+ $out = preg_replace("/^[ \t]*\n/m", "\n", $out);
+ // Removes leading and ending blank lines.
+ $out = trim($out, "\n");
+ return $out;
+ }
+
+ /**
+ * Removes any unique tokens from the input.
+ *
+ * @param string $text The input to clean
+ * @return string Cleaned input
+ * @since 3.5.5
+ */
+
+ protected function cleanUniqueTokens($text)
+ {
+ return str_replace($this->uid, '', $text);
+ }
+
+ /**
+ * Uses the specified callback method to format the content between end and start nodes.
+ *
+ * @param string $text The input to format
+ * @param string $start The start node to look for
+ * @param string $end The end node to look for
+ * @param string $method The callback method
+ * @return string Processed input
+ */
+
+ protected function doSpecial($text, $start, $end, $method)
+ {
+ return preg_replace_callback(
+ '/(?P^|\s|[|[({>])'.preg_quote($start, '/').'(?P.*?)'.preg_quote($end, '/').'/ms',
+ array(&$this, $method),
+ $text
+ );
+ }
+
+ /**
+ * Parses notextile tags in the given input.
+ *
+ * @param string $text The input
+ * @return string Processed input
+ */
+
+ protected function noTextile($text)
+ {
+ $text = $this->doSpecial($text, '', ' ', 'fTextile');
+ return $this->doSpecial($text, '==', '==', 'fTextile');
+ }
+
+ /**
+ * Format notextile blocks.
+ *
+ * @param array $m Options
+ * @return string
+ */
+
+ protected function fTextile($m)
+ {
+ return $m['before'].$this->shelve($m['content']);
+ }
+
+ /**
+ * Parses footnote reference links in the given input.
+ *
+ * This method replaces [n] instances with links.
+ *
+ * @param string $text The input
+ * @return string $text Processed input
+ * @see Parser::footnoteID()
+ */
+
+ protected function footnoteRefs($text)
+ {
+ return preg_replace_callback(
+ '/(?<=\S)\[(?P'.$this->regex_snippets['digit'].'+)'.
+ '(?P!?)\]'.$this->regex_snippets['space'].'?/U'.$this->regex_snippets['mod'],
+ array(&$this, 'footnoteID'),
+ $text
+ );
+ }
+
+ /**
+ * Renders a footnote reference link or ID.
+ *
+ * @param array $m Options
+ * @return string Footnote link, or ID
+ */
+
+ protected function footnoteID($m)
+ {
+ $backref = ' class="footnote"';
+
+ if (empty($this->fn[$m['id']])) {
+ $this->fn[$m['id']] = $id = $this->linkPrefix . ($this->linkIndex++);
+ $backref .= " id=\"fnrev$id\"";
+ }
+
+ $fnid = $this->fn[$m['id']];
+ $footref = ('!' == $m['nolink']) ? $m['id'] : ''.$m['id'].' ';
+ $footref = $this->formatFootnote($footref, $backref, false);
+
+ return $footref;
+ }
+
+ /**
+ * Parses and shelves quoted quotes in the given input.
+ *
+ * @param string $text The text to search for quoted quotes
+ * @return string
+ */
+
+ protected function glyphQuotedQuote($text, $find = '"?|"[^"]+"')
+ {
+ return preg_replace_callback(
+ "/ (?P{$this->quote_starts})(?P$find)(?P.) /".$this->regex_snippets['mod'],
+ array(&$this, "fGlyphQuotedQuote"),
+ $text
+ );
+ }
+
+ /**
+ * Formats quoted quotes and stores it on the shelf.
+ *
+ * @param array $m Named regular expression parts
+ * @return string Input with quoted quotes removed and replaced with tokens
+ * @see Parser::glyphQuotedQuote()
+ */
+
+ protected function fGlyphQuotedQuote($m)
+ {
+ // Check the correct closing character was found.
+ if (!isset($this->quotes[$m['pre']]) || $m['post'] !== $this->quotes[$m['pre']]) {
+ return $m[0];
+ }
+
+ $pre = strtr($m['pre'], array(
+ '"' => '“',
+ "'" => '‘',
+ ' ' => ' ',
+ ));
+
+ $post = strtr($m['post'], array(
+ '"' => '”',
+ "'" => '’',
+ ' ' => ' ',
+ ));
+
+ $found = $m['quoted'];
+ if (strlen($found) > 1) {
+ $found = rtrim($this->glyphs($m['quoted']));
+ } elseif ('"' === $found) {
+ $found = """;
+ }
+
+ $glyph = ' '.$pre.$found.$post.' ';
+ return $this->shelve($glyph);
+ }
+
+ /**
+ * Replaces glyphs in the given input.
+ *
+ * This method performs typographical glyph replacements. The input is split
+ * across HTML-like tags in order to avoid attempting glyph
+ * replacements within tags.
+ *
+ * @param string $text Input Textile
+ * @return string
+ */
+
+ protected function glyphs($text)
+ {
+ // Fix: hackish -- adds a space if final char of text is a double quote.
+ $text = preg_replace('/"\z/', "\" ", $text);
+
+ $text = preg_split("@(<[\w/!?].*>)@Us".$this->regex_snippets['mod'], $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $i = 0;
+ foreach ($text as $line) {
+ // Text tag text tag text ...
+ if (++$i % 2) {
+ // Raw < > & chars are already entity encoded in restricted mode
+ if (!$this->restricted) {
+ $line = preg_replace('/&(?!#?[a-z0-9]+;)/i', '&', $line);
+ $line = str_replace(array('<', '>'), array('<', '>'), $line);
+ }
+ $line = preg_replace($this->glyph_search, $this->glyph_replace, $line);
+ }
+ $glyph_out[] = $line;
+ }
+ return join('', $glyph_out);
+ }
+
+ /**
+ * Replaces glyph references in the given input.
+ *
+ * This method removes temporary glyph: instances
+ * from the input.
+ *
+ * @param string $text The input
+ * @return string Processed input
+ */
+
+ protected function replaceGlyphs($text)
+ {
+ return str_replace($this->uid.':glyph:', '', $text);
+ }
+
+ /**
+ * Translates alignment tag into corresponding CSS text-align property value.
+ *
+ * @param string $in The Textile alignment tag
+ * @return string CSS text-align value
+ */
+
+ protected function hAlign($in)
+ {
+ $vals = array(
+ '<' => 'left',
+ '>' => 'right',
+ '<>' => 'justify',
+ '<' => 'left',
+ '=' => 'center',
+ '>' => 'right',
+ '<>' => 'justify',
+ );
+
+ return (isset($vals[$in])) ? $vals[$in] : '';
+ }
+
+ /**
+ * Translates vertical alignment tag into corresponding CSS vertical-align property value.
+ *
+ * @param string $in The Textile alignment tag
+ * @return string CSS vertical-align value
+ */
+
+ protected function vAlign($in)
+ {
+ $vals = array(
+ '^' => 'top',
+ '-' => 'middle',
+ '~' => 'bottom',
+ );
+
+ return (isset($vals[$in])) ? $vals[$in] : '';
+ }
+
+ /**
+ * Converts character codes in the given input from HTML numeric character reference to character code.
+ *
+ * Conversion is done according to Textile's multi-byte conversion map.
+ *
+ * @param string $text The input
+ * @param string $charset The character set
+ * @return string Processed input
+ */
+
+ protected function encodeHigh($text, $charset = 'UTF-8')
+ {
+ if ($this->isMultiByteStringSupported()) {
+ return mb_encode_numericentity($text, $this->cmap, $charset);
+ }
+
+ return htmlentities($text, ENT_NOQUOTES, $charset);
+ }
+
+ /**
+ * Converts numeric HTML character references to character code.
+ *
+ * @param string $text The input
+ * @param string $charset The character set
+ * @return string Processed input
+ */
+
+ protected function decodeHigh($text, $charset = 'UTF-8')
+ {
+ $text = (string) intval($text) === (string) $text ? "$text;" : "&$text;";
+
+ if ($this->isMultiByteStringSupported()) {
+ return mb_decode_numericentity($text, $this->cmap, $charset);
+ }
+
+ return html_entity_decode($text, ENT_NOQUOTES, $charset);
+ }
+
+ /**
+ * Convert special characters to HTML entities.
+ *
+ * This method's functionality is identical to PHP's own
+ * htmlspecialchars(). In Textile this is used for sanitising
+ * the input.
+ *
+ * @param string $str The string to encode
+ * @param bool $quotes Encode quotes
+ * @return string Encoded string
+ * @see htmlspecialchars()
+ */
+
+ protected function encodeHTML($str, $quotes = true)
+ {
+ $a = array(
+ '&' => '&',
+ '<' => '<',
+ '>' => '>',
+ );
+
+ if ($quotes) {
+ $a = $a + array(
+ "'" => ''', // Numeric, as in htmlspecialchars
+ '"' => '"',
+ );
+ }
+
+ return str_replace(array_keys($a), $a, $str);
+ }
+
+ /**
+ * Convert special characters to HTML entities.
+ *
+ * This is identical to encodeHTML(), but takes restricted
+ * mode into account. When in restricted mode, only escapes
+ * quotes.
+ *
+ * @param string $str The string to encode
+ * @param bool $quotes Encode quotes
+ * @return string Encoded string
+ * @see Parser::encodeHTML()
+ */
+
+ protected function rEncodeHTML($str, $quotes = true)
+ {
+ // In restricted mode, all input but quotes has already been escaped
+ if ($this->restricted) {
+ return str_replace('"', '"', $str);
+ }
+
+ return $this->encodeHTML($str, $quotes);
+ }
+
+ /**
+ * Whether multiple mbstring extensions is loaded.
+ *
+ * @return bool
+ * @since 3.5.5
+ */
+
+ protected function isMultiByteStringSupported()
+ {
+ if ($this->mb === null) {
+ $this->mb = is_callable('mb_strlen');
+ }
+
+ return $this->mb;
+ }
+
+ /**
+ * Whether PCRE supports UTF-8.
+ *
+ * @return bool
+ * @since 3.5.5
+ */
+
+ protected function isUnicodePcreSupported()
+ {
+ return (bool) @preg_match('/\pL/u', 'a');
+ }
+}
diff --git a/app/lib/Netcarver/Textile/Tag.php b/app/lib/Netcarver/Textile/Tag.php
new file mode 100644
index 00000000..3a35f88d
--- /dev/null
+++ b/app/lib/Netcarver/Textile/Tag.php
@@ -0,0 +1,118 @@
+
+ * use Netcarver\Textile\Tag;
+ * $img = new Tag('img');
+ * echo (string) $img->class('big blue')->src('images/elephant.jpg');
+ *
+ */
+
+class Tag extends DataBag
+{
+ /**
+ * The name of the tag.
+ *
+ * @var string
+ */
+
+ protected $tag;
+
+ /**
+ * Whether the tag is self-closing.
+ *
+ * @var bool
+ */
+
+ protected $selfclose;
+
+ /**
+ * Constructor.
+ *
+ * @param string $name The tag name
+ * @param array $attributes An array of attributes
+ * @param bool $selfclosing Whether the tag is self-closing
+ */
+
+ public function __construct($name, array $attributes = null, $selfclosing = true)
+ {
+ parent::__construct($attributes);
+ $this->tag = $name;
+ $this->selfclose = $selfclosing;
+ }
+
+ /**
+ * Returns the tag as HTML.
+ *
+ *
+ * $img = new Tag('img');
+ * $img->src('images/example.jpg')->alt('Example image');
+ * echo (string) $img;
+ *
+ *
+ * @return string A HTML element
+ */
+
+ public function __toString()
+ {
+ $attributes = '';
+
+ if ($this->data) {
+ ksort($this->data);
+ foreach ($this->data as $name => $value) {
+ $attributes .= " $name=\"$value\"";
+ }
+ }
+
+ if ($this->tag) {
+ return '<' . $this->tag . $attributes . (($this->selfclose) ? " />" : '>');
+ }
+
+ return $attributes;
+ }
+}
diff --git a/app/lib/PHPMailer.inc b/app/lib/PHPMailer.inc
new file mode 100644
index 00000000..cceaf430
--- /dev/null
+++ b/app/lib/PHPMailer.inc
@@ -0,0 +1,3453 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\lib
+ {
+
+
+ /**
+ * Class to ensure that Compatibility library below is loaded.
+ *
+ * @author Oliver Hanraths
+ */
+ class PHPMailer
+ {
+
+ /**
+ * Call this function to ensure this file is loaded.
+ */
+ public static function load()
+ {
+ }
+
+ }
+
+ }
+
+ namespace {
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * PHP Version 5
+ * @package PHPMailer
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ * @author Marcus Bointon (Synchro/coolbru)
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * @package PHPMailer
+ * @author Marcus Bointon (Synchro/coolbru)
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ */
+class PHPMailer
+{
+ /**
+ * The PHPMailer Version number.
+ * @type string
+ */
+ public $Version = '5.2.8';
+
+ /**
+ * Email priority.
+ * Options: 1 = High, 3 = Normal, 5 = low.
+ * @type int
+ */
+ public $Priority = 3;
+
+ /**
+ * The character set of the message.
+ * @type string
+ */
+ public $CharSet = 'iso-8859-1';
+
+ /**
+ * The MIME Content-type of the message.
+ * @type string
+ */
+ public $ContentType = 'text/plain';
+
+ /**
+ * The message encoding.
+ * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
+ * @type string
+ */
+ public $Encoding = '8bit';
+
+ /**
+ * Holds the most recent mailer error message.
+ * @type string
+ */
+ public $ErrorInfo = '';
+
+ /**
+ * The From email address for the message.
+ * @type string
+ */
+ public $From = 'root@localhost';
+
+ /**
+ * The From name of the message.
+ * @type string
+ */
+ public $FromName = 'Root User';
+
+ /**
+ * The Sender email (Return-Path) of the message.
+ * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
+ * @type string
+ */
+ public $Sender = '';
+
+ /**
+ * The Return-Path of the message.
+ * If empty, it will be set to either From or Sender.
+ * @type string
+ * @deprecated Email senders should never set a return-path header;
+ * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
+ * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
+ */
+ public $ReturnPath = '';
+
+ /**
+ * The Subject of the message.
+ * @type string
+ */
+ public $Subject = '';
+
+ /**
+ * An HTML or plain text message body.
+ * If HTML then call isHTML(true).
+ * @type string
+ */
+ public $Body = '';
+
+ /**
+ * The plain-text message body.
+ * This body can be read by mail clients that do not have HTML email
+ * capability such as mutt & Eudora.
+ * Clients that can read HTML will view the normal Body.
+ * @type string
+ */
+ public $AltBody = '';
+
+ /**
+ * An iCal message part body.
+ * Only supported in simple alt or alt_inline message types
+ * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
+ * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
+ * @link http://kigkonsult.se/iCalcreator/
+ * @type string
+ */
+ public $Ical = '';
+
+ /**
+ * The complete compiled MIME message body.
+ * @access protected
+ * @type string
+ */
+ protected $MIMEBody = '';
+
+ /**
+ * The complete compiled MIME message headers.
+ * @type string
+ * @access protected
+ */
+ protected $MIMEHeader = '';
+
+ /**
+ * Extra headers that createHeader() doesn't fold in.
+ * @type string
+ * @access protected
+ */
+ protected $mailHeader = '';
+
+ /**
+ * Word-wrap the message body to this number of chars.
+ * @type int
+ */
+ public $WordWrap = 0;
+
+ /**
+ * Which method to use to send mail.
+ * Options: "mail", "sendmail", or "smtp".
+ * @type string
+ */
+ public $Mailer = 'mail';
+
+ /**
+ * The path to the sendmail program.
+ * @type string
+ */
+ public $Sendmail = '/usr/sbin/sendmail';
+
+ /**
+ * Whether mail() uses a fully sendmail-compatible MTA.
+ * One which supports sendmail's "-oi -f" options.
+ * @type bool
+ */
+ public $UseSendmailOptions = true;
+
+ /**
+ * Path to PHPMailer plugins.
+ * Useful if the SMTP class is not in the PHP include path.
+ * @type string
+ * @deprecated Should not be needed now there is an autoloader.
+ */
+ public $PluginDir = '';
+
+ /**
+ * The email address that a reading confirmation should be sent to.
+ * @type string
+ */
+ public $ConfirmReadingTo = '';
+
+ /**
+ * The hostname to use in Message-Id and Received headers
+ * and as default HELO string.
+ * If empty, the value returned
+ * by SERVER_NAME is used or 'localhost.localdomain'.
+ * @type string
+ */
+ public $Hostname = '';
+
+ /**
+ * An ID to be used in the Message-Id header.
+ * If empty, a unique id will be generated.
+ * @type string
+ */
+ public $MessageID = '';
+
+ /**
+ * The message Date to be used in the Date header.
+ * If empty, the current date will be added.
+ * @type string
+ */
+ public $MessageDate = '';
+
+ /**
+ * SMTP hosts.
+ * Either a single hostname or multiple semicolon-delimited hostnames.
+ * You can also specify a different port
+ * for each host by using this format: [hostname:port]
+ * (e.g. "smtp1.example.com:25;smtp2.example.com").
+ * Hosts will be tried in order.
+ * @type string
+ */
+ public $Host = 'localhost';
+
+ /**
+ * The default SMTP server port.
+ * @type int
+ * @Todo Why is this needed when the SMTP class takes care of it?
+ */
+ public $Port = 25;
+
+ /**
+ * The SMTP HELO of the message.
+ * Default is $Hostname.
+ * @type string
+ * @see PHPMailer::$Hostname
+ */
+ public $Helo = '';
+
+ /**
+ * The secure connection prefix.
+ * Options: "", "ssl" or "tls"
+ * @type string
+ */
+ public $SMTPSecure = '';
+
+ /**
+ * Whether to use SMTP authentication.
+ * Uses the Username and Password properties.
+ * @type bool
+ * @see PHPMailer::$Username
+ * @see PHPMailer::$Password
+ */
+ public $SMTPAuth = false;
+
+ /**
+ * SMTP username.
+ * @type string
+ */
+ public $Username = '';
+
+ /**
+ * SMTP password.
+ * @type string
+ */
+ public $Password = '';
+
+ /**
+ * SMTP auth type.
+ * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
+ * @type string
+ */
+ public $AuthType = '';
+
+ /**
+ * SMTP realm.
+ * Used for NTLM auth
+ * @type string
+ */
+ public $Realm = '';
+
+ /**
+ * SMTP workstation.
+ * Used for NTLM auth
+ * @type string
+ */
+ public $Workstation = '';
+
+ /**
+ * The SMTP server timeout in seconds.
+ * @type int
+ */
+ public $Timeout = 10;
+
+ /**
+ * SMTP class debug output mode.
+ * Options:
+ * 0: no output
+ * 1: commands
+ * 2: data and commands
+ * 3: as 2 plus connection status
+ * 4: low level data output
+ * @type int
+ * @see SMTP::$do_debug
+ */
+ public $SMTPDebug = 0;
+
+ /**
+ * How to handle debug output.
+ * Options:
+ * 'echo': Output plain-text as-is, appropriate for CLI
+ * 'html': Output escaped, line breaks converted to , appropriate for browser output
+ * 'error_log': Output to error log as configured in php.ini
+ * @type string
+ * @see SMTP::$Debugoutput
+ */
+ public $Debugoutput = 'echo';
+
+ /**
+ * Whether to keep SMTP connection open after each message.
+ * If this is set to true then to close the connection
+ * requires an explicit call to smtpClose().
+ * @type bool
+ */
+ public $SMTPKeepAlive = false;
+
+ /**
+ * Whether to split multiple to addresses into multiple messages
+ * or send them all in one message.
+ * @type bool
+ */
+ public $SingleTo = false;
+
+ /**
+ * Storage for addresses when SingleTo is enabled.
+ * @type array
+ * @todo This should really not be public
+ */
+ public $SingleToArray = array();
+
+ /**
+ * Whether to generate VERP addresses on send.
+ * Only applicable when sending via SMTP.
+ * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Postfix VERP info
+ * @type bool
+ */
+ public $do_verp = false;
+
+ /**
+ * Whether to allow sending messages with an empty body.
+ * @type bool
+ */
+ public $AllowEmpty = false;
+
+ /**
+ * The default line ending.
+ * @note The default remains "\n". We force CRLF where we know
+ * it must be used via self::CRLF.
+ * @type string
+ */
+ public $LE = "\n";
+
+ /**
+ * DKIM selector.
+ * @type string
+ */
+ public $DKIM_selector = '';
+
+ /**
+ * DKIM Identity.
+ * Usually the email address used as the source of the email
+ * @type string
+ */
+ public $DKIM_identity = '';
+
+ /**
+ * DKIM passphrase.
+ * Used if your key is encrypted.
+ * @type string
+ */
+ public $DKIM_passphrase = '';
+
+ /**
+ * DKIM signing domain name.
+ * @example 'example.com'
+ * @type string
+ */
+ public $DKIM_domain = '';
+
+ /**
+ * DKIM private key file path.
+ * @type string
+ */
+ public $DKIM_private = '';
+
+ /**
+ * Callback Action function name.
+ *
+ * The function that handles the result of the send email action.
+ * It is called out by send() for each email sent.
+ *
+ * Value can be any php callable: http://www.php.net/is_callable
+ *
+ * Parameters:
+ * bool $result result of the send action
+ * string $to email address of the recipient
+ * string $cc cc email addresses
+ * string $bcc bcc email addresses
+ * string $subject the subject
+ * string $body the email body
+ * string $from email address of sender
+ * @type string
+ */
+ public $action_function = '';
+
+ /**
+ * What to use in the X-Mailer header.
+ * Options: null for default, whitespace for none, or a string to use
+ * @type string
+ */
+ public $XMailer = '';
+
+ /**
+ * An instance of the SMTP sender class.
+ * @type SMTP
+ * @access protected
+ */
+ protected $smtp = null;
+
+ /**
+ * The array of 'to' addresses.
+ * @type array
+ * @access protected
+ */
+ protected $to = array();
+
+ /**
+ * The array of 'cc' addresses.
+ * @type array
+ * @access protected
+ */
+ protected $cc = array();
+
+ /**
+ * The array of 'bcc' addresses.
+ * @type array
+ * @access protected
+ */
+ protected $bcc = array();
+
+ /**
+ * The array of reply-to names and addresses.
+ * @type array
+ * @access protected
+ */
+ protected $ReplyTo = array();
+
+ /**
+ * An array of all kinds of addresses.
+ * Includes all of $to, $cc, $bcc, $replyto
+ * @type array
+ * @access protected
+ */
+ protected $all_recipients = array();
+
+ /**
+ * The array of attachments.
+ * @type array
+ * @access protected
+ */
+ protected $attachment = array();
+
+ /**
+ * The array of custom headers.
+ * @type array
+ * @access protected
+ */
+ protected $CustomHeader = array();
+
+ /**
+ * The most recent Message-ID (including angular brackets).
+ * @type string
+ * @access protected
+ */
+ protected $lastMessageID = '';
+
+ /**
+ * The message's MIME type.
+ * @type string
+ * @access protected
+ */
+ protected $message_type = '';
+
+ /**
+ * The array of MIME boundary strings.
+ * @type array
+ * @access protected
+ */
+ protected $boundary = array();
+
+ /**
+ * The array of available languages.
+ * @type array
+ * @access protected
+ */
+ protected $language = array();
+
+ /**
+ * The number of errors encountered.
+ * @type integer
+ * @access protected
+ */
+ protected $error_count = 0;
+
+ /**
+ * The S/MIME certificate file path.
+ * @type string
+ * @access protected
+ */
+ protected $sign_cert_file = '';
+
+ /**
+ * The S/MIME key file path.
+ * @type string
+ * @access protected
+ */
+ protected $sign_key_file = '';
+
+ /**
+ * The S/MIME password for the key.
+ * Used only if the key is encrypted.
+ * @type string
+ * @access protected
+ */
+ protected $sign_key_pass = '';
+
+ /**
+ * Whether to throw exceptions for errors.
+ * @type bool
+ * @access protected
+ */
+ protected $exceptions = false;
+
+ /**
+ * Error severity: message only, continue processing
+ */
+ const STOP_MESSAGE = 0;
+
+ /**
+ * Error severity: message, likely ok to continue processing
+ */
+ const STOP_CONTINUE = 1;
+
+ /**
+ * Error severity: message, plus full stop, critical error reached
+ */
+ const STOP_CRITICAL = 2;
+
+ /**
+ * SMTP RFC standard line ending
+ */
+ const CRLF = "\r\n";
+
+ /**
+ * Constructor
+ * @param bool $exceptions Should we throw external exceptions?
+ */
+ public function __construct($exceptions = false)
+ {
+ $this->exceptions = ($exceptions == true);
+ //Make sure our autoloader is loaded
+ if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
+ $autoload = spl_autoload_functions();
+ if ($autoload === false or !in_array('PHPMailerAutoload', $autoload)) {
+ require 'PHPMailerAutoload.php';
+ }
+ }
+ }
+
+ /**
+ * Destructor.
+ */
+ public function __destruct()
+ {
+ if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
+ $this->smtpClose();
+ }
+ }
+
+ /**
+ * Call mail() in a safe_mode-aware fashion.
+ * Also, unless sendmail_path points to sendmail (or something that
+ * claims to be sendmail), don't pass params (not a perfect fix,
+ * but it will do)
+ * @param string $to To
+ * @param string $subject Subject
+ * @param string $body Message Body
+ * @param string $header Additional Header(s)
+ * @param string $params Params
+ * @access private
+ * @return bool
+ */
+ private function mailPassthru($to, $subject, $body, $header, $params)
+ {
+ //Check overloading of mail function to avoid double-encoding
+ if (ini_get('mbstring.func_overload') & 1) {
+ $subject = $this->secureHeader($subject);
+ } else {
+ $subject = $this->encodeHeader($this->secureHeader($subject));
+ }
+ if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
+ $result = @mail($to, $subject, $body, $header);
+ } else {
+ $result = @mail($to, $subject, $body, $header, $params);
+ }
+ return $result;
+ }
+
+ /**
+ * Output debugging info via user-defined method.
+ * Only if debug output is enabled.
+ * @see PHPMailer::$Debugoutput
+ * @see PHPMailer::$SMTPDebug
+ * @param string $str
+ */
+ protected function edebug($str)
+ {
+ if (!$this->SMTPDebug) {
+ return;
+ }
+ switch ($this->Debugoutput) {
+ case 'error_log':
+ error_log($str);
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking display that's HTML-safe
+ echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . " \n";
+ break;
+ case 'echo':
+ default:
+ echo $str."\n";
+ }
+ }
+
+ /**
+ * Sets message type to HTML or plain.
+ * @param bool $ishtml True for HTML mode.
+ * @return void
+ */
+ public function isHTML($ishtml = true)
+ {
+ if ($ishtml) {
+ $this->ContentType = 'text/html';
+ } else {
+ $this->ContentType = 'text/plain';
+ }
+ }
+
+ /**
+ * Send messages using SMTP.
+ * @return void
+ */
+ public function isSMTP()
+ {
+ $this->Mailer = 'smtp';
+ }
+
+ /**
+ * Send messages using PHP's mail() function.
+ * @return void
+ */
+ public function isMail()
+ {
+ $this->Mailer = 'mail';
+ }
+
+ /**
+ * Send messages using $Sendmail.
+ * @return void
+ */
+ public function isSendmail()
+ {
+ $ini_sendmail_path = ini_get('sendmail_path');
+
+ if (!stristr($ini_sendmail_path, 'sendmail')) {
+ $this->Sendmail = '/usr/sbin/sendmail';
+ } else {
+ $this->Sendmail = $ini_sendmail_path;
+ }
+ $this->Mailer = 'sendmail';
+ }
+
+ /**
+ * Send messages using qmail.
+ * @return void
+ */
+ public function isQmail()
+ {
+ $ini_sendmail_path = ini_get('sendmail_path');
+
+ if (!stristr($ini_sendmail_path, 'qmail')) {
+ $this->Sendmail = '/var/qmail/bin/qmail-inject';
+ } else {
+ $this->Sendmail = $ini_sendmail_path;
+ }
+ $this->Mailer = 'qmail';
+ }
+
+ /**
+ * Add a "To" address.
+ * @param string $address
+ * @param string $name
+ * @return bool true on success, false if address already used
+ */
+ public function addAddress($address, $name = '')
+ {
+ return $this->addAnAddress('to', $address, $name);
+ }
+
+ /**
+ * Add a "CC" address.
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+ * @param string $address
+ * @param string $name
+ * @return bool true on success, false if address already used
+ */
+ public function addCC($address, $name = '')
+ {
+ return $this->addAnAddress('cc', $address, $name);
+ }
+
+ /**
+ * Add a "BCC" address.
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+ * @param string $address
+ * @param string $name
+ * @return bool true on success, false if address already used
+ */
+ public function addBCC($address, $name = '')
+ {
+ return $this->addAnAddress('bcc', $address, $name);
+ }
+
+ /**
+ * Add a "Reply-to" address.
+ * @param string $address
+ * @param string $name
+ * @return bool
+ */
+ public function addReplyTo($address, $name = '')
+ {
+ return $this->addAnAddress('Reply-To', $address, $name);
+ }
+
+ /**
+ * Add an address to one of the recipient arrays.
+ * Addresses that have been added already return false, but do not throw exceptions
+ * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
+ * @param string $address The email address to send to
+ * @param string $name
+ * @throws phpmailerException
+ * @return bool true on success, false if address already used or invalid in some way
+ * @access protected
+ */
+ protected function addAnAddress($kind, $address, $name = '')
+ {
+ if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
+ $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
+ $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
+ if ($this->exceptions) {
+ throw new phpmailerException('Invalid recipient array: ' . $kind);
+ }
+ return false;
+ }
+ $address = trim($address);
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+ if (!$this->validateAddress($address)) {
+ $this->setError($this->lang('invalid_address') . ': ' . $address);
+ $this->edebug($this->lang('invalid_address') . ': ' . $address);
+ if ($this->exceptions) {
+ throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
+ }
+ return false;
+ }
+ if ($kind != 'Reply-To') {
+ if (!isset($this->all_recipients[strtolower($address)])) {
+ array_push($this->$kind, array($address, $name));
+ $this->all_recipients[strtolower($address)] = true;
+ return true;
+ }
+ } else {
+ if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
+ $this->ReplyTo[strtolower($address)] = array($address, $name);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set the From and FromName properties.
+ * @param string $address
+ * @param string $name
+ * @param bool $auto Whether to also set the Sender address, defaults to true
+ * @throws phpmailerException
+ * @return bool
+ */
+ public function setFrom($address, $name = '', $auto = true)
+ {
+ $address = trim($address);
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+ if (!$this->validateAddress($address)) {
+ $this->setError($this->lang('invalid_address') . ': ' . $address);
+ $this->edebug($this->lang('invalid_address') . ': ' . $address);
+ if ($this->exceptions) {
+ throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
+ }
+ return false;
+ }
+ $this->From = $address;
+ $this->FromName = $name;
+ if ($auto) {
+ if (empty($this->Sender)) {
+ $this->Sender = $address;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the Message-ID header of the last email.
+ * Technically this is the value from the last time the headers were created,
+ * but it's also the message ID of the last sent message except in
+ * pathological cases.
+ * @return string
+ */
+ public function getLastMessageID()
+ {
+ return $this->lastMessageID;
+ }
+
+ /**
+ * Check that a string looks like an email address.
+ * @param string $address The email address to check
+ * @param string $patternselect A selector for the validation pattern to use :
+ * * `auto` Pick strictest one automatically;
+ * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
+ * * `pcre` Use old PCRE implementation;
+ * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
+ * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
+ * * `noregex` Don't use a regex: super fast, really dumb.
+ * @return bool
+ * @static
+ * @access public
+ */
+ public static function validateAddress($address, $patternselect = 'auto')
+ {
+ if (!$patternselect or $patternselect == 'auto') {
+ if (defined('PCRE_VERSION')) { //Check this constant so it works when extension_loaded() is disabled
+ if (version_compare(PCRE_VERSION, '8.0') >= 0) {
+ $patternselect = 'pcre8';
+ } else {
+ $patternselect = 'pcre';
+ }
+ } else {
+ //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
+ if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+ $patternselect = 'php';
+ } else {
+ $patternselect = 'noregex';
+ }
+ }
+ }
+ switch ($patternselect) {
+ case 'pcre8':
+ /**
+ * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
+ * @link http://squiloople.com/2009/12/20/email-address-validation/
+ * @copyright 2009-2010 Michael Rushton
+ * Feel free to use and redistribute this code. But please keep this copyright notice.
+ */
+ return (bool)preg_match(
+ '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
+ '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
+ '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
+ '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
+ '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
+ '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
+ '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
+ '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+ '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
+ $address
+ );
+ break;
+ case 'pcre':
+ //An older regex that doesn't need a recent PCRE
+ return (bool)preg_match(
+ '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
+ '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
+ '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
+ '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
+ '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
+ '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
+ '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
+ '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
+ '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+ '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
+ $address
+ );
+ break;
+ case 'html5':
+ /**
+ * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
+ * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
+ */
+ return (bool)preg_match('/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
+ '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', $address);
+ break;
+ case 'php':
+ default:
+ return (bool)filter_var($address, FILTER_VALIDATE_EMAIL);
+ break;
+ case 'noregex':
+ //No PCRE! Do something _very_ approximate!
+ //Check the address is 3 chars or longer and contains an @ that's not the first or last char
+ return (strlen($address) >= 3
+ and strpos($address, '@') >= 1
+ and strpos($address, '@') != strlen($address) - 1);
+ break;
+ }
+ }
+
+ /**
+ * Create a message and send it.
+ * Uses the sending method specified by $Mailer.
+ * @throws phpmailerException
+ * @return bool false on error - See the ErrorInfo property for details of the error.
+ */
+ public function send()
+ {
+ try {
+ if (!$this->preSend()) {
+ return false;
+ }
+ return $this->postSend();
+ } catch (phpmailerException $exc) {
+ $this->mailHeader = '';
+ $this->setError($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Prepare a message for sending.
+ * @throws phpmailerException
+ * @return bool
+ */
+ public function preSend()
+ {
+ try {
+ $this->mailHeader = '';
+ if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
+ throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
+ }
+
+ // Set whether the message is multipart/alternative
+ if (!empty($this->AltBody)) {
+ $this->ContentType = 'multipart/alternative';
+ }
+
+ $this->error_count = 0; // reset errors
+ $this->setMessageType();
+ // Refuse to send an empty message unless we are specifically allowing it
+ if (!$this->AllowEmpty and empty($this->Body)) {
+ throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
+ }
+
+ $this->MIMEHeader = $this->createHeader();
+ $this->MIMEBody = $this->createBody();
+
+ // To capture the complete message when using mail(), create
+ // an extra header list which createHeader() doesn't fold in
+ if ($this->Mailer == 'mail') {
+ if (count($this->to) > 0) {
+ $this->mailHeader .= $this->addrAppend('To', $this->to);
+ } else {
+ $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
+ }
+ $this->mailHeader .= $this->headerLine(
+ 'Subject',
+ $this->encodeHeader($this->secureHeader(trim($this->Subject)))
+ );
+ }
+
+ // Sign with DKIM if enabled
+ if (!empty($this->DKIM_domain)
+ && !empty($this->DKIM_private)
+ && !empty($this->DKIM_selector)
+ && !empty($this->DKIM_domain)
+ && file_exists($this->DKIM_private)) {
+ $header_dkim = $this->DKIM_Add(
+ $this->MIMEHeader . $this->mailHeader,
+ $this->encodeHeader($this->secureHeader($this->Subject)),
+ $this->MIMEBody
+ );
+ $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
+ str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
+ }
+ return true;
+
+ } catch (phpmailerException $exc) {
+ $this->setError($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Actually send a message.
+ * Send the email via the selected mechanism
+ * @throws phpmailerException
+ * @return bool
+ */
+ public function postSend()
+ {
+ try {
+ // Choose the mailer and send through it
+ switch ($this->Mailer) {
+ case 'sendmail':
+ case 'qmail':
+ return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
+ case 'smtp':
+ return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
+ case 'mail':
+ return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+ default:
+ $sendMethod = $this->Mailer.'Send';
+ if (method_exists($this, $sendMethod)) {
+ return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
+ }
+
+ return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+ }
+ } catch (phpmailerException $exc) {
+ $this->setError($exc->getMessage());
+ $this->edebug($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Send mail using the $Sendmail program.
+ * @param string $header The message headers
+ * @param string $body The message body
+ * @see PHPMailer::$Sendmail
+ * @throws phpmailerException
+ * @access protected
+ * @return bool
+ */
+ protected function sendmailSend($header, $body)
+ {
+ if ($this->Sender != '') {
+ if ($this->Mailer == 'qmail') {
+ $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+ } else {
+ $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+ }
+ } else {
+ if ($this->Mailer == 'qmail') {
+ $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
+ } else {
+ $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
+ }
+ }
+ if ($this->SingleTo === true) {
+ foreach ($this->SingleToArray as $val) {
+ if (!@$mail = popen($sendmail, 'w')) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ fputs($mail, 'To: ' . $val . "\n");
+ fputs($mail, $header);
+ fputs($mail, $body);
+ $result = pclose($mail);
+ // implement call back function if it exists
+ $isSent = ($result == 0) ? 1 : 0;
+ $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+ if ($result != 0) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ }
+ } else {
+ if (!@$mail = popen($sendmail, 'w')) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ fputs($mail, $header);
+ fputs($mail, $body);
+ $result = pclose($mail);
+ // implement call back function if it exists
+ $isSent = ($result == 0) ? 1 : 0;
+ $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+ if ($result != 0) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Send mail using the PHP mail() function.
+ * @param string $header The message headers
+ * @param string $body The message body
+ * @link http://www.php.net/manual/en/book.mail.php
+ * @throws phpmailerException
+ * @access protected
+ * @return bool
+ */
+ protected function mailSend($header, $body)
+ {
+ $toArr = array();
+ foreach ($this->to as $toaddr) {
+ $toArr[] = $this->addrFormat($toaddr);
+ }
+ $to = implode(', ', $toArr);
+
+ if (empty($this->Sender)) {
+ $params = ' ';
+ } else {
+ $params = sprintf('-f%s', $this->Sender);
+ }
+ if ($this->Sender != '' and !ini_get('safe_mode')) {
+ $old_from = ini_get('sendmail_from');
+ ini_set('sendmail_from', $this->Sender);
+ }
+ $result = false;
+ if ($this->SingleTo === true && count($toArr) > 1) {
+ foreach ($toArr as $val) {
+ $result = $this->mailPassthru($val, $this->Subject, $body, $header, $params);
+ // implement call back function if it exists
+ $isSent = ($result == 1) ? 1 : 0;
+ $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+ }
+ } else {
+ $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
+ // implement call back function if it exists
+ $isSent = ($result == 1) ? 1 : 0;
+ $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+ }
+ if (isset($old_from)) {
+ ini_set('sendmail_from', $old_from);
+ }
+ if (!$result) {
+ throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
+ }
+ return true;
+ }
+
+ /**
+ * Get an instance to use for SMTP operations.
+ * Override this function to load your own SMTP implementation
+ * @return SMTP
+ */
+ public function getSMTPInstance()
+ {
+ if (!is_object($this->smtp)) {
+ $this->smtp = new SMTP;
+ }
+ return $this->smtp;
+ }
+
+ /**
+ * Send mail via SMTP.
+ * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
+ * Uses the PHPMailerSMTP class by default.
+ * @see PHPMailer::getSMTPInstance() to use a different class.
+ * @param string $header The message headers
+ * @param string $body The message body
+ * @throws phpmailerException
+ * @uses SMTP
+ * @access protected
+ * @return bool
+ */
+ protected function smtpSend($header, $body)
+ {
+ $bad_rcpt = array();
+
+ if (!$this->smtpConnect()) {
+ throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
+ }
+ $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
+ if (!$this->smtp->mail($smtp_from)) {
+ $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
+ throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
+ }
+
+ // Attempt to send to all recipients
+ foreach ($this->to as $to) {
+ if (!$this->smtp->recipient($to[0])) {
+ $bad_rcpt[] = $to[0];
+ $isSent = 0;
+ } else {
+ $isSent = 1;
+ }
+ $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From);
+ }
+ foreach ($this->cc as $cc) {
+ if (!$this->smtp->recipient($cc[0])) {
+ $bad_rcpt[] = $cc[0];
+ $isSent = 0;
+ } else {
+ $isSent = 1;
+ }
+ $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From);
+ }
+ foreach ($this->bcc as $bcc) {
+ if (!$this->smtp->recipient($bcc[0])) {
+ $bad_rcpt[] = $bcc[0];
+ $isSent = 0;
+ } else {
+ $isSent = 1;
+ }
+ $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From);
+ }
+
+ //Only send the DATA command if we have viable recipients
+ if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
+ throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
+ }
+ if ($this->SMTPKeepAlive == true) {
+ $this->smtp->reset();
+ } else {
+ $this->smtp->quit();
+ $this->smtp->close();
+ }
+ if (count($bad_rcpt) > 0) { //Create error message for any bad addresses
+ throw new phpmailerException(
+ $this->lang('recipients_failed') . implode(', ', $bad_rcpt),
+ self::STOP_CONTINUE
+ );
+ }
+ return true;
+ }
+
+ /**
+ * Initiate a connection to an SMTP server.
+ * Returns false if the operation failed.
+ * @param array $options An array of options compatible with stream_context_create()
+ * @uses SMTP
+ * @access public
+ * @throws phpmailerException
+ * @return bool
+ */
+ public function smtpConnect($options = array())
+ {
+ if (is_null($this->smtp)) {
+ $this->smtp = $this->getSMTPInstance();
+ }
+
+ //Already connected?
+ if ($this->smtp->connected()) {
+ return true;
+ }
+
+ $this->smtp->setTimeout($this->Timeout);
+ $this->smtp->setDebugLevel($this->SMTPDebug);
+ $this->smtp->setDebugOutput($this->Debugoutput);
+ $this->smtp->setVerp($this->do_verp);
+ $hosts = explode(';', $this->Host);
+ $lastexception = null;
+
+ foreach ($hosts as $hostentry) {
+ $hostinfo = array();
+ if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
+ //Not a valid host entry
+ continue;
+ }
+ //$hostinfo[2]: optional ssl or tls prefix
+ //$hostinfo[3]: the hostname
+ //$hostinfo[4]: optional port number
+ //The host string prefix can temporarily override the current setting for SMTPSecure
+ //If it's not specified, the default value is used
+ $prefix = '';
+ $tls = ($this->SMTPSecure == 'tls');
+ if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
+ $prefix = 'ssl://';
+ $tls = false; //Can't have SSL and TLS at once
+ } elseif ($hostinfo[2] == 'tls') {
+ $tls = true;
+ //tls doesn't use a prefix
+ }
+ $host = $hostinfo[3];
+ $port = $this->Port;
+ $tport = (integer)$hostinfo[4];
+ if ($tport > 0 and $tport < 65536) {
+ $port = $tport;
+ }
+ if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
+ try {
+ if ($this->Helo) {
+ $hello = $this->Helo;
+ } else {
+ $hello = $this->serverHostname();
+ }
+ $this->smtp->hello($hello);
+
+ if ($tls) {
+ if (!$this->smtp->startTLS()) {
+ throw new phpmailerException($this->lang('connect_host'));
+ }
+ //We must resend HELO after tls negotiation
+ $this->smtp->hello($hello);
+ }
+ if ($this->SMTPAuth) {
+ if (!$this->smtp->authenticate(
+ $this->Username,
+ $this->Password,
+ $this->AuthType,
+ $this->Realm,
+ $this->Workstation
+ )
+ ) {
+ throw new phpmailerException($this->lang('authenticate'));
+ }
+ }
+ return true;
+ } catch (phpmailerException $exc) {
+ $lastexception = $exc;
+ //We must have connected, but then failed TLS or Auth, so close connection nicely
+ $this->smtp->quit();
+ }
+ }
+ }
+ //If we get here, all connection attempts have failed, so close connection hard
+ $this->smtp->close();
+ //As we've caught all exceptions, just report whatever the last one was
+ if ($this->exceptions and !is_null($lastexception)) {
+ throw $lastexception;
+ }
+ return false;
+ }
+
+ /**
+ * Close the active SMTP session if one exists.
+ * @return void
+ */
+ public function smtpClose()
+ {
+ if ($this->smtp !== null) {
+ if ($this->smtp->connected()) {
+ $this->smtp->quit();
+ $this->smtp->close();
+ }
+ }
+ }
+
+ /**
+ * Set the language for error messages.
+ * Returns false if it cannot load the language file.
+ * The default language is English.
+ * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
+ * @param string $lang_path Path to the language file directory, with trailing separator (slash)
+ * @return bool
+ * @access public
+ */
+ public function setLanguage($langcode = 'en', $lang_path = '')
+ {
+ //Define full set of translatable strings in English
+ $PHPMAILER_LANG = array(
+ 'authenticate' => 'SMTP Error: Could not authenticate.',
+ 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
+ 'data_not_accepted' => 'SMTP Error: data not accepted.',
+ 'empty_message' => 'Message body empty',
+ 'encoding' => 'Unknown encoding: ',
+ 'execute' => 'Could not execute: ',
+ 'file_access' => 'Could not access file: ',
+ 'file_open' => 'File Error: Could not open file: ',
+ 'from_failed' => 'The following From address failed: ',
+ 'instantiate' => 'Could not instantiate mail function.',
+ 'invalid_address' => 'Invalid address',
+ 'mailer_not_supported' => ' mailer is not supported.',
+ 'provide_address' => 'You must provide at least one recipient email address.',
+ 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
+ 'signing' => 'Signing Error: ',
+ 'smtp_connect_failed' => 'SMTP connect() failed.',
+ 'smtp_error' => 'SMTP server error: ',
+ 'variable_set' => 'Cannot set or reset variable: '
+ );
+ if (empty($lang_path)) {
+ //Calculate an absolute path so it can work if CWD is not here
+ $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
+ }
+ $foundlang = true;
+ $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
+ if ($langcode != 'en') { //There is no English translation file
+ //Make sure language file path is readable
+ if (!is_readable($lang_file)) {
+ $foundlang = false;
+ } else {
+ //Overwrite language-specific strings.
+ //This way we'll never have missing translations.
+ $foundlang = include $lang_file;
+ }
+ }
+ $this->language = $PHPMAILER_LANG;
+ return ($foundlang == true); //Returns false if language not found
+ }
+
+ /**
+ * Get the array of strings for the current language.
+ * @return array
+ */
+ public function getTranslations()
+ {
+ return $this->language;
+ }
+
+ /**
+ * Create recipient headers.
+ * @access public
+ * @param string $type
+ * @param array $addr An array of recipient,
+ * where each recipient is a 2-element indexed array with element 0 containing an address
+ * and element 1 containing a name, like:
+ * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
+ * @return string
+ */
+ public function addrAppend($type, $addr)
+ {
+ $addresses = array();
+ foreach ($addr as $address) {
+ $addresses[] = $this->addrFormat($address);
+ }
+ return $type . ': ' . implode(', ', $addresses) . $this->LE;
+ }
+
+ /**
+ * Format an address for use in a message header.
+ * @access public
+ * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
+ * like array('joe@example.com', 'Joe User')
+ * @return string
+ */
+ public function addrFormat($addr)
+ {
+ if (empty($addr[1])) { // No name provided
+ return $this->secureHeader($addr[0]);
+ } else {
+ return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
+ $addr[0]
+ ) . '>';
+ }
+ }
+
+ /**
+ * Word-wrap message.
+ * For use with mailers that do not automatically perform wrapping
+ * and for quoted-printable encoded messages.
+ * Original written by philippe.
+ * @param string $message The message to wrap
+ * @param integer $length The line length to wrap to
+ * @param bool $qp_mode Whether to run in Quoted-Printable mode
+ * @access public
+ * @return string
+ */
+ public function wrapText($message, $length, $qp_mode = false)
+ {
+ $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
+ // If utf-8 encoding is used, we will need to make sure we don't
+ // split multibyte characters when we wrap
+ $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
+ $lelen = strlen($this->LE);
+ $crlflen = strlen(self::CRLF);
+
+ $message = $this->fixEOL($message);
+ if (substr($message, -$lelen) == $this->LE) {
+ $message = substr($message, 0, -$lelen);
+ }
+
+ $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
+ $message = '';
+ for ($i = 0; $i < count($line); $i++) {
+ $line_part = explode(' ', $line[$i]);
+ $buf = '';
+ for ($e = 0; $e < count($line_part); $e++) {
+ $word = $line_part[$e];
+ if ($qp_mode and (strlen($word) > $length)) {
+ $space_left = $length - strlen($buf) - $crlflen;
+ if ($e != 0) {
+ if ($space_left > 20) {
+ $len = $space_left;
+ if ($is_utf8) {
+ $len = $this->utf8CharBoundary($word, $len);
+ } elseif (substr($word, $len - 1, 1) == '=') {
+ $len--;
+ } elseif (substr($word, $len - 2, 1) == '=') {
+ $len -= 2;
+ }
+ $part = substr($word, 0, $len);
+ $word = substr($word, $len);
+ $buf .= ' ' . $part;
+ $message .= $buf . sprintf('=%s', self::CRLF);
+ } else {
+ $message .= $buf . $soft_break;
+ }
+ $buf = '';
+ }
+ while (strlen($word) > 0) {
+ if ($length <= 0) {
+ break;
+ }
+ $len = $length;
+ if ($is_utf8) {
+ $len = $this->utf8CharBoundary($word, $len);
+ } elseif (substr($word, $len - 1, 1) == '=') {
+ $len--;
+ } elseif (substr($word, $len - 2, 1) == '=') {
+ $len -= 2;
+ }
+ $part = substr($word, 0, $len);
+ $word = substr($word, $len);
+
+ if (strlen($word) > 0) {
+ $message .= $part . sprintf('=%s', self::CRLF);
+ } else {
+ $buf = $part;
+ }
+ }
+ } else {
+ $buf_o = $buf;
+ $buf .= ($e == 0) ? $word : (' ' . $word);
+
+ if (strlen($buf) > $length and $buf_o != '') {
+ $message .= $buf_o . $soft_break;
+ $buf = $word;
+ }
+ }
+ }
+ $message .= $buf . self::CRLF;
+ }
+
+ return $message;
+ }
+
+ /**
+ * Find the last character boundary prior to $maxLength in a utf-8
+ * quoted (printable) encoded string.
+ * Original written by Colin Brown.
+ * @access public
+ * @param string $encodedText utf-8 QP text
+ * @param int $maxLength find last character boundary prior to this length
+ * @return int
+ */
+ public function utf8CharBoundary($encodedText, $maxLength)
+ {
+ $foundSplitPos = false;
+ $lookBack = 3;
+ while (!$foundSplitPos) {
+ $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
+ $encodedCharPos = strpos($lastChunk, '=');
+ if ($encodedCharPos !== false) {
+ // Found start of encoded character byte within $lookBack block.
+ // Check the encoded byte value (the 2 chars after the '=')
+ $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
+ $dec = hexdec($hex);
+ if ($dec < 128) { // Single byte character.
+ // If the encoded char was found at pos 0, it will fit
+ // otherwise reduce maxLength to start of the encoded char
+ $maxLength = ($encodedCharPos == 0) ? $maxLength :
+ $maxLength - ($lookBack - $encodedCharPos);
+ $foundSplitPos = true;
+ } elseif ($dec >= 192) { // First byte of a multi byte character
+ // Reduce maxLength to split at start of character
+ $maxLength = $maxLength - ($lookBack - $encodedCharPos);
+ $foundSplitPos = true;
+ } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
+ $lookBack += 3;
+ }
+ } else {
+ // No encoded character found
+ $foundSplitPos = true;
+ }
+ }
+ return $maxLength;
+ }
+
+ /**
+ * Set the body wrapping.
+ * @access public
+ * @return void
+ */
+ public function setWordWrap()
+ {
+ if ($this->WordWrap < 1) {
+ return;
+ }
+
+ switch ($this->message_type) {
+ case 'alt':
+ case 'alt_inline':
+ case 'alt_attach':
+ case 'alt_inline_attach':
+ $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
+ break;
+ default:
+ $this->Body = $this->wrapText($this->Body, $this->WordWrap);
+ break;
+ }
+ }
+
+ /**
+ * Assemble message headers.
+ * @access public
+ * @return string The assembled headers
+ */
+ public function createHeader()
+ {
+ $result = '';
+
+ // Set the boundaries
+ $uniq_id = md5(uniqid(time()));
+ $this->boundary[1] = 'b1_' . $uniq_id;
+ $this->boundary[2] = 'b2_' . $uniq_id;
+ $this->boundary[3] = 'b3_' . $uniq_id;
+
+ if ($this->MessageDate == '') {
+ $this->MessageDate = self::rfcDate();
+ }
+ $result .= $this->headerLine('Date', $this->MessageDate);
+
+
+ // To be created automatically by mail()
+ if ($this->SingleTo === true) {
+ if ($this->Mailer != 'mail') {
+ foreach ($this->to as $toaddr) {
+ $this->SingleToArray[] = $this->addrFormat($toaddr);
+ }
+ }
+ } else {
+ if (count($this->to) > 0) {
+ if ($this->Mailer != 'mail') {
+ $result .= $this->addrAppend('To', $this->to);
+ }
+ } elseif (count($this->cc) == 0) {
+ $result .= $this->headerLine('To', 'undisclosed-recipients:;');
+ }
+ }
+
+ $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
+
+ // sendmail and mail() extract Cc from the header before sending
+ if (count($this->cc) > 0) {
+ $result .= $this->addrAppend('Cc', $this->cc);
+ }
+
+ // sendmail and mail() extract Bcc from the header before sending
+ if ((
+ $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
+ )
+ and count($this->bcc) > 0
+ ) {
+ $result .= $this->addrAppend('Bcc', $this->bcc);
+ }
+
+ if (count($this->ReplyTo) > 0) {
+ $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
+ }
+
+ // mail() sets the subject itself
+ if ($this->Mailer != 'mail') {
+ $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
+ }
+
+ if ($this->MessageID != '') {
+ $this->lastMessageID = $this->MessageID;
+ } else {
+ $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
+ }
+ $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
+ $result .= $this->headerLine('X-Priority', $this->Priority);
+ if ($this->XMailer == '') {
+ $result .= $this->headerLine(
+ 'X-Mailer',
+ 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
+ );
+ } else {
+ $myXmailer = trim($this->XMailer);
+ if ($myXmailer) {
+ $result .= $this->headerLine('X-Mailer', $myXmailer);
+ }
+ }
+
+ if ($this->ConfirmReadingTo != '') {
+ $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
+ }
+
+ // Add custom headers
+ for ($index = 0; $index < count($this->CustomHeader); $index++) {
+ $result .= $this->headerLine(
+ trim($this->CustomHeader[$index][0]),
+ $this->encodeHeader(trim($this->CustomHeader[$index][1]))
+ );
+ }
+ if (!$this->sign_key_file) {
+ $result .= $this->headerLine('MIME-Version', '1.0');
+ $result .= $this->getMailMIME();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the message MIME type headers.
+ * @access public
+ * @return string
+ */
+ public function getMailMIME()
+ {
+ $result = '';
+ $ismultipart = true;
+ switch ($this->message_type) {
+ case 'inline':
+ $result .= $this->headerLine('Content-Type', 'multipart/related;');
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+ break;
+ case 'attach':
+ case 'inline_attach':
+ case 'alt_attach':
+ case 'alt_inline_attach':
+ $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+ break;
+ case 'alt':
+ case 'alt_inline':
+ $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+ break;
+ default:
+ // Catches case 'plain': and case '':
+ $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
+ $ismultipart = false;
+ break;
+ }
+ //RFC1341 part 5 says 7bit is assumed if not specified
+ if ($this->Encoding != '7bit') {
+ //RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
+ if ($ismultipart) {
+ if ($this->Encoding == '8bit') {
+ $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
+ }
+ //The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
+ } else {
+ $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
+ }
+ }
+
+ if ($this->Mailer != 'mail') {
+ $result .= $this->LE;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the whole MIME message.
+ * Includes complete headers and body.
+ * Only valid post preSend().
+ * @see PHPMailer::preSend()
+ * @access public
+ * @return string
+ */
+ public function getSentMIMEMessage()
+ {
+ return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
+ }
+
+
+ /**
+ * Assemble the message body.
+ * Returns an empty string on failure.
+ * @access public
+ * @throws phpmailerException
+ * @return string The assembled message body
+ */
+ public function createBody()
+ {
+ $body = '';
+
+ if ($this->sign_key_file) {
+ $body .= $this->getMailMIME() . $this->LE;
+ }
+
+ $this->setWordWrap();
+
+ $bodyEncoding = $this->Encoding;
+ $bodyCharSet = $this->CharSet;
+ if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
+ $bodyEncoding = '7bit';
+ $bodyCharSet = 'us-ascii';
+ }
+ $altBodyEncoding = $this->Encoding;
+ $altBodyCharSet = $this->CharSet;
+ if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
+ $altBodyEncoding = '7bit';
+ $altBodyCharSet = 'us-ascii';
+ }
+ switch ($this->message_type) {
+ case 'inline':
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[1]);
+ break;
+ case 'attach':
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ case 'inline_attach':
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ case 'alt':
+ $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ if (!empty($this->Ical)) {
+ $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
+ $body .= $this->encodeString($this->Ical, $this->Encoding);
+ $body .= $this->LE . $this->LE;
+ }
+ $body .= $this->endBoundary($this->boundary[1]);
+ break;
+ case 'alt_inline':
+ $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->endBoundary($this->boundary[1]);
+ break;
+ case 'alt_attach':
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->endBoundary($this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ case 'alt_inline_attach':
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->textLine('--' . $this->boundary[2]);
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[3]);
+ $body .= $this->LE;
+ $body .= $this->endBoundary($this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ default:
+ // catch case 'plain' and case ''
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ break;
+ }
+
+ if ($this->isError()) {
+ $body = '';
+ } elseif ($this->sign_key_file) {
+ try {
+ if (!defined('PKCS7_TEXT')) {
+ throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
+ }
+ //TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
+ $file = tempnam(sys_get_temp_dir(), 'mail');
+ file_put_contents($file, $body); //TODO check this worked
+ $signed = tempnam(sys_get_temp_dir(), 'signed');
+ if (@openssl_pkcs7_sign(
+ $file,
+ $signed,
+ 'file://' . realpath($this->sign_cert_file),
+ array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
+ null
+ )
+ ) {
+ @unlink($file);
+ $body = file_get_contents($signed);
+ @unlink($signed);
+ } else {
+ @unlink($file);
+ @unlink($signed);
+ throw new phpmailerException($this->lang('signing') . openssl_error_string());
+ }
+ } catch (phpmailerException $exc) {
+ $body = '';
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ }
+ }
+ return $body;
+ }
+
+ /**
+ * Return the start of a message boundary.
+ * @access protected
+ * @param string $boundary
+ * @param string $charSet
+ * @param string $contentType
+ * @param string $encoding
+ * @return string
+ */
+ protected function getBoundary($boundary, $charSet, $contentType, $encoding)
+ {
+ $result = '';
+ if ($charSet == '') {
+ $charSet = $this->CharSet;
+ }
+ if ($contentType == '') {
+ $contentType = $this->ContentType;
+ }
+ if ($encoding == '') {
+ $encoding = $this->Encoding;
+ }
+ $result .= $this->textLine('--' . $boundary);
+ $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
+ $result .= $this->LE;
+ //RFC1341 part 5 says 7bit is assumed if not specified
+ if ($encoding != '7bit') {
+ $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
+ }
+ $result .= $this->LE;
+
+ return $result;
+ }
+
+ /**
+ * Return the end of a message boundary.
+ * @access protected
+ * @param string $boundary
+ * @return string
+ */
+ protected function endBoundary($boundary)
+ {
+ return $this->LE . '--' . $boundary . '--' . $this->LE;
+ }
+
+ /**
+ * Set the message type.
+ * PHPMailer only supports some preset message types,
+ * not arbitrary MIME structures.
+ * @access protected
+ * @return void
+ */
+ protected function setMessageType()
+ {
+ $this->message_type = array();
+ if ($this->alternativeExists()) {
+ $this->message_type[] = 'alt';
+ }
+ if ($this->inlineImageExists()) {
+ $this->message_type[] = 'inline';
+ }
+ if ($this->attachmentExists()) {
+ $this->message_type[] = 'attach';
+ }
+ $this->message_type = implode('_', $this->message_type);
+ if ($this->message_type == '') {
+ $this->message_type = 'plain';
+ }
+ }
+
+ /**
+ * Format a header line.
+ * @access public
+ * @param string $name
+ * @param string $value
+ * @return string
+ */
+ public function headerLine($name, $value)
+ {
+ return $name . ': ' . $value . $this->LE;
+ }
+
+ /**
+ * Return a formatted mail line.
+ * @access public
+ * @param string $value
+ * @return string
+ */
+ public function textLine($value)
+ {
+ return $value . $this->LE;
+ }
+
+ /**
+ * Add an attachment from a path on the filesystem.
+ * Returns false if the file could not be found or read.
+ * @param string $path Path to the attachment.
+ * @param string $name Overrides the attachment name.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File extension (MIME) type.
+ * @param string $disposition Disposition to use
+ * @throws phpmailerException
+ * @return bool
+ */
+ public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
+ {
+ try {
+ if (!@is_file($path)) {
+ throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
+ }
+
+ //If a MIME type is not specified, try to work it out from the file name
+ if ($type == '') {
+ $type = self::filenameToType($path);
+ }
+
+ $filename = basename($path);
+ if ($name == '') {
+ $name = $filename;
+ }
+
+ $this->attachment[] = array(
+ 0 => $path,
+ 1 => $filename,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => false, // isStringAttachment
+ 6 => $disposition,
+ 7 => 0
+ );
+
+ } catch (phpmailerException $exc) {
+ $this->setError($exc->getMessage());
+ $this->edebug($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return the array of attachments.
+ * @return array
+ */
+ public function getAttachments()
+ {
+ return $this->attachment;
+ }
+
+ /**
+ * Attach all file, string, and binary attachments to the message.
+ * Returns an empty string on failure.
+ * @access protected
+ * @param string $disposition_type
+ * @param string $boundary
+ * @return string
+ */
+ protected function attachAll($disposition_type, $boundary)
+ {
+ // Return text of body
+ $mime = array();
+ $cidUniq = array();
+ $incl = array();
+
+ // Add all attachments
+ foreach ($this->attachment as $attachment) {
+ // Check if it is a valid disposition_filter
+ if ($attachment[6] == $disposition_type) {
+ // Check for string attachment
+ $string = '';
+ $path = '';
+ $bString = $attachment[5];
+ if ($bString) {
+ $string = $attachment[0];
+ } else {
+ $path = $attachment[0];
+ }
+
+ $inclhash = md5(serialize($attachment));
+ if (in_array($inclhash, $incl)) {
+ continue;
+ }
+ $incl[] = $inclhash;
+ $name = $attachment[2];
+ $encoding = $attachment[3];
+ $type = $attachment[4];
+ $disposition = $attachment[6];
+ $cid = $attachment[7];
+ if ($disposition == 'inline' && isset($cidUniq[$cid])) {
+ continue;
+ }
+ $cidUniq[$cid] = true;
+
+ $mime[] = sprintf('--%s%s', $boundary, $this->LE);
+ $mime[] = sprintf(
+ 'Content-Type: %s; name="%s"%s',
+ $type,
+ $this->encodeHeader($this->secureHeader($name)),
+ $this->LE
+ );
+ //RFC1341 part 5 says 7bit is assumed if not specified
+ if ($encoding != '7bit') {
+ $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
+ }
+
+ if ($disposition == 'inline') {
+ $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
+ }
+
+ // If a filename contains any of these chars, it should be quoted,
+ // but not otherwise: RFC2183 & RFC2045 5.1
+ // Fixes a warning in IETF's msglint MIME checker
+ // Allow for bypassing the Content-Disposition header totally
+ if (!(empty($disposition))) {
+ if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s; filename="%s"%s',
+ $disposition,
+ $this->encodeHeader($this->secureHeader($name)),
+ $this->LE . $this->LE
+ );
+ } else {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s; filename=%s%s',
+ $disposition,
+ $this->encodeHeader($this->secureHeader($name)),
+ $this->LE . $this->LE
+ );
+ }
+ } else {
+ $mime[] = $this->LE;
+ }
+
+ // Encode as string attachment
+ if ($bString) {
+ $mime[] = $this->encodeString($string, $encoding);
+ if ($this->isError()) {
+ return '';
+ }
+ $mime[] = $this->LE . $this->LE;
+ } else {
+ $mime[] = $this->encodeFile($path, $encoding);
+ if ($this->isError()) {
+ return '';
+ }
+ $mime[] = $this->LE . $this->LE;
+ }
+ }
+ }
+
+ $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
+
+ return implode('', $mime);
+ }
+
+ /**
+ * Encode a file attachment in requested format.
+ * Returns an empty string on failure.
+ * @param string $path The full path to the file
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+ * @throws phpmailerException
+ * @see EncodeFile(encodeFile
+ * @access protected
+ * @return string
+ */
+ protected function encodeFile($path, $encoding = 'base64')
+ {
+ try {
+ if (!is_readable($path)) {
+ throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
+ }
+ $magic_quotes = get_magic_quotes_runtime();
+ if ($magic_quotes) {
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ set_magic_quotes_runtime(0);
+ } else {
+ ini_set('magic_quotes_runtime', 0);
+ }
+ }
+ $file_buffer = file_get_contents($path);
+ $file_buffer = $this->encodeString($file_buffer, $encoding);
+ if ($magic_quotes) {
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ set_magic_quotes_runtime($magic_quotes);
+ } else {
+ ini_set('magic_quotes_runtime', $magic_quotes);
+ }
+ }
+ return $file_buffer;
+ } catch (Exception $exc) {
+ $this->setError($exc->getMessage());
+ return '';
+ }
+ }
+
+ /**
+ * Encode a string in requested format.
+ * Returns an empty string on failure.
+ * @param string $str The text to encode
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+ * @access public
+ * @return string
+ */
+ public function encodeString($str, $encoding = 'base64')
+ {
+ $encoded = '';
+ switch (strtolower($encoding)) {
+ case 'base64':
+ $encoded = chunk_split(base64_encode($str), 76, $this->LE);
+ break;
+ case '7bit':
+ case '8bit':
+ $encoded = $this->fixEOL($str);
+ //Make sure it ends with a line break
+ if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
+ $encoded .= $this->LE;
+ }
+ break;
+ case 'binary':
+ $encoded = $str;
+ break;
+ case 'quoted-printable':
+ $encoded = $this->encodeQP($str);
+ break;
+ default:
+ $this->setError($this->lang('encoding') . $encoding);
+ break;
+ }
+ return $encoded;
+ }
+
+ /**
+ * Encode a header string optimally.
+ * Picks shortest of Q, B, quoted-printable or none.
+ * @access public
+ * @param string $str
+ * @param string $position
+ * @return string
+ */
+ public function encodeHeader($str, $position = 'text')
+ {
+ $matchcount = 0;
+ switch (strtolower($position)) {
+ case 'phrase':
+ if (!preg_match('/[\200-\377]/', $str)) {
+ // Can't use addslashes as we don't know the value of magic_quotes_sybase
+ $encoded = addcslashes($str, "\0..\37\177\\\"");
+ if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
+ return ($encoded);
+ } else {
+ return ("\"$encoded\"");
+ }
+ }
+ $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
+ break;
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case 'comment':
+ $matchcount = preg_match_all('/[()"]/', $str, $matches);
+ // Intentional fall-through
+ case 'text':
+ default:
+ $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
+ break;
+ }
+
+ if ($matchcount == 0) { //There are no chars that need encoding
+ return ($str);
+ }
+
+ $maxlen = 75 - 7 - strlen($this->CharSet);
+ // Try to select the encoding which should produce the shortest output
+ if ($matchcount > strlen($str) / 3) {
+ //More than a third of the content will need encoding, so B encoding will be most efficient
+ $encoding = 'B';
+ if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
+ // Use a custom function which correctly encodes and wraps long
+ // multibyte strings without breaking lines within a character
+ $encoded = $this->base64EncodeWrapMB($str, "\n");
+ } else {
+ $encoded = base64_encode($str);
+ $maxlen -= $maxlen % 4;
+ $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
+ }
+ } else {
+ $encoding = 'Q';
+ $encoded = $this->encodeQ($str, $position);
+ $encoded = $this->wrapText($encoded, $maxlen, true);
+ $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
+ }
+
+ $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
+ $encoded = trim(str_replace("\n", $this->LE, $encoded));
+
+ return $encoded;
+ }
+
+ /**
+ * Check if a string contains multi-byte characters.
+ * @access public
+ * @param string $str multi-byte text to wrap encode
+ * @return bool
+ */
+ public function hasMultiBytes($str)
+ {
+ if (function_exists('mb_strlen')) {
+ return (strlen($str) > mb_strlen($str, $this->CharSet));
+ } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
+ return false;
+ }
+ }
+
+ /**
+ * Does a string contain any 8-bit chars (in any charset)?
+ * @param string $text
+ * @return bool
+ */
+ public function has8bitChars($text)
+ {
+ return (bool)preg_match('/[\x80-\xFF]/', $text);
+ }
+
+ /**
+ * Encode and wrap long multibyte strings for mail headers
+ * without breaking lines within a character.
+ * Adapted from a function by paravoid
+ * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
+ * @access public
+ * @param string $str multi-byte text to wrap encode
+ * @param string $linebreak string to use as linefeed/end-of-line
+ * @return string
+ */
+ public function base64EncodeWrapMB($str, $linebreak = null)
+ {
+ $start = '=?' . $this->CharSet . '?B?';
+ $end = '?=';
+ $encoded = '';
+ if ($linebreak === null) {
+ $linebreak = $this->LE;
+ }
+
+ $mb_length = mb_strlen($str, $this->CharSet);
+ // Each line must have length <= 75, including $start and $end
+ $length = 75 - strlen($start) - strlen($end);
+ // Average multi-byte ratio
+ $ratio = $mb_length / strlen($str);
+ // Base64 has a 4:3 ratio
+ $avgLength = floor($length * $ratio * .75);
+
+ for ($i = 0; $i < $mb_length; $i += $offset) {
+ $lookBack = 0;
+ do {
+ $offset = $avgLength - $lookBack;
+ $chunk = mb_substr($str, $i, $offset, $this->CharSet);
+ $chunk = base64_encode($chunk);
+ $lookBack++;
+ } while (strlen($chunk) > $length);
+ $encoded .= $chunk . $linebreak;
+ }
+
+ // Chomp the last linefeed
+ $encoded = substr($encoded, 0, -strlen($linebreak));
+ return $encoded;
+ }
+
+ /**
+ * Encode a string in quoted-printable format.
+ * According to RFC2045 section 6.7.
+ * @access public
+ * @param string $string The text to encode
+ * @param integer $line_max Number of chars allowed on a line before wrapping
+ * @return string
+ * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
+ */
+ public function encodeQP($string, $line_max = 76)
+ {
+ if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
+ return $this->fixEOL(quoted_printable_encode($string));
+ }
+ //Fall back to a pure PHP implementation
+ $string = str_replace(
+ array('%20', '%0D%0A.', '%0D%0A', '%'),
+ array(' ', "\r\n=2E", "\r\n", '='),
+ rawurlencode($string)
+ );
+ $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
+ return $this->fixEOL($string);
+ }
+
+ /**
+ * Backward compatibility wrapper for an old QP encoding function that was removed.
+ * @see PHPMailer::encodeQP()
+ * @access public
+ * @param string $string
+ * @param integer $line_max
+ * @param bool $space_conv
+ * @return string
+ * @deprecated Use encodeQP instead.
+ */
+ public function encodeQPphp(
+ $string,
+ $line_max = 76,
+ /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
+ ) {
+ return $this->encodeQP($string, $line_max);
+ }
+
+ /**
+ * Encode a string using Q encoding.
+ * @link http://tools.ietf.org/html/rfc2047
+ * @param string $str the text to encode
+ * @param string $position Where the text is going to be used, see the RFC for what that means
+ * @access public
+ * @return string
+ */
+ public function encodeQ($str, $position = 'text')
+ {
+ //There should not be any EOL in the string
+ $pattern = '';
+ $encoded = str_replace(array("\r", "\n"), '', $str);
+ switch (strtolower($position)) {
+ case 'phrase':
+ //RFC 2047 section 5.3
+ $pattern = '^A-Za-z0-9!*+\/ -';
+ break;
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case 'comment':
+ //RFC 2047 section 5.2
+ $pattern = '\(\)"';
+ //intentional fall-through
+ //for this reason we build the $pattern without including delimiters and []
+ case 'text':
+ default:
+ //RFC 2047 section 5.1
+ //Replace every high ascii, control, =, ? and _ characters
+ $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
+ break;
+ }
+ $matches = array();
+ if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
+ //If the string contains an '=', make sure it's the first thing we replace
+ //so as to avoid double-encoding
+ $eqkey = array_search('=', $matches[0]);
+ if ($eqkey !== false) {
+ unset($matches[0][$eqkey]);
+ array_unshift($matches[0], '=');
+ }
+ foreach (array_unique($matches[0]) as $char) {
+ $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
+ }
+ }
+ //Replace every spaces to _ (more readable than =20)
+ return str_replace(' ', '_', $encoded);
+ }
+
+
+ /**
+ * Add a string or binary attachment (non-filesystem).
+ * This method can be used to attach ascii or binary data,
+ * such as a BLOB record from a database.
+ * @param string $string String attachment data.
+ * @param string $filename Name of the attachment.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File extension (MIME) type.
+ * @param string $disposition Disposition to use
+ * @return void
+ */
+ public function addStringAttachment(
+ $string,
+ $filename,
+ $encoding = 'base64',
+ $type = '',
+ $disposition = 'attachment'
+ ) {
+ //If a MIME type is not specified, try to work it out from the file name
+ if ($type == '') {
+ $type = self::filenameToType($filename);
+ }
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $string,
+ 1 => $filename,
+ 2 => basename($filename),
+ 3 => $encoding,
+ 4 => $type,
+ 5 => true, // isStringAttachment
+ 6 => $disposition,
+ 7 => 0
+ );
+ }
+
+ /**
+ * Add an embedded (inline) attachment from a file.
+ * This can include images, sounds, and just about any other document type.
+ * These differ from 'regular' attachmants in that they are intended to be
+ * displayed inline with the message, not just attached for download.
+ * This is used in HTML messages that embed the images
+ * the HTML refers to using the $cid value.
+ * @param string $path Path to the attachment.
+ * @param string $cid Content ID of the attachment; Use this to reference
+ * the content when using an embedded image in HTML.
+ * @param string $name Overrides the attachment name.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File MIME type.
+ * @param string $disposition Disposition to use
+ * @return bool True on successfully adding an attachment
+ */
+ public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
+ {
+ if (!@is_file($path)) {
+ $this->setError($this->lang('file_access') . $path);
+ return false;
+ }
+
+ //If a MIME type is not specified, try to work it out from the file name
+ if ($type == '') {
+ $type = self::filenameToType($path);
+ }
+
+ $filename = basename($path);
+ if ($name == '') {
+ $name = $filename;
+ }
+
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $path,
+ 1 => $filename,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => false, // isStringAttachment
+ 6 => $disposition,
+ 7 => $cid
+ );
+ return true;
+ }
+
+ /**
+ * Add an embedded stringified attachment.
+ * This can include images, sounds, and just about any other document type.
+ * Be sure to set the $type to an image type for images:
+ * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
+ * @param string $string The attachment binary data.
+ * @param string $cid Content ID of the attachment; Use this to reference
+ * the content when using an embedded image in HTML.
+ * @param string $name
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type MIME type.
+ * @param string $disposition Disposition to use
+ * @return bool True on successfully adding an attachment
+ */
+ public function addStringEmbeddedImage(
+ $string,
+ $cid,
+ $name = '',
+ $encoding = 'base64',
+ $type = '',
+ $disposition = 'inline'
+ ) {
+ //If a MIME type is not specified, try to work it out from the name
+ if ($type == '') {
+ $type = self::filenameToType($name);
+ }
+
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $string,
+ 1 => $name,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => true, // isStringAttachment
+ 6 => $disposition,
+ 7 => $cid
+ );
+ return true;
+ }
+
+ /**
+ * Check if an inline attachment is present.
+ * @access public
+ * @return bool
+ */
+ public function inlineImageExists()
+ {
+ foreach ($this->attachment as $attachment) {
+ if ($attachment[6] == 'inline') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if an attachment (non-inline) is present.
+ * @return bool
+ */
+ public function attachmentExists()
+ {
+ foreach ($this->attachment as $attachment) {
+ if ($attachment[6] == 'attachment') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if this message has an alternative body set.
+ * @return bool
+ */
+ public function alternativeExists()
+ {
+ return !empty($this->AltBody);
+ }
+
+ /**
+ * Clear all To recipients.
+ * @return void
+ */
+ public function clearAddresses()
+ {
+ foreach ($this->to as $to) {
+ unset($this->all_recipients[strtolower($to[0])]);
+ }
+ $this->to = array();
+ }
+
+ /**
+ * Clear all CC recipients.
+ * @return void
+ */
+ public function clearCCs()
+ {
+ foreach ($this->cc as $cc) {
+ unset($this->all_recipients[strtolower($cc[0])]);
+ }
+ $this->cc = array();
+ }
+
+ /**
+ * Clear all BCC recipients.
+ * @return void
+ */
+ public function clearBCCs()
+ {
+ foreach ($this->bcc as $bcc) {
+ unset($this->all_recipients[strtolower($bcc[0])]);
+ }
+ $this->bcc = array();
+ }
+
+ /**
+ * Clear all ReplyTo recipients.
+ * @return void
+ */
+ public function clearReplyTos()
+ {
+ $this->ReplyTo = array();
+ }
+
+ /**
+ * Clear all recipient types.
+ * @return void
+ */
+ public function clearAllRecipients()
+ {
+ $this->to = array();
+ $this->cc = array();
+ $this->bcc = array();
+ $this->all_recipients = array();
+ }
+
+ /**
+ * Clear all filesystem, string, and binary attachments.
+ * @return void
+ */
+ public function clearAttachments()
+ {
+ $this->attachment = array();
+ }
+
+ /**
+ * Clear all custom headers.
+ * @return void
+ */
+ public function clearCustomHeaders()
+ {
+ $this->CustomHeader = array();
+ }
+
+ /**
+ * Add an error message to the error container.
+ * @access protected
+ * @param string $msg
+ * @return void
+ */
+ protected function setError($msg)
+ {
+ $this->error_count++;
+ if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
+ $lasterror = $this->smtp->getError();
+ if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
+ $msg .= '' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "
\n";
+ }
+ }
+ $this->ErrorInfo = $msg;
+ }
+
+ /**
+ * Return an RFC 822 formatted date.
+ * @access public
+ * @return string
+ * @static
+ */
+ public static function rfcDate()
+ {
+ //Set the time zone to whatever the default is to avoid 500 errors
+ //Will default to UTC if it's not set properly in php.ini
+ date_default_timezone_set(@date_default_timezone_get());
+ return date('D, j M Y H:i:s O');
+ }
+
+ /**
+ * Get the server hostname.
+ * Returns 'localhost.localdomain' if unknown.
+ * @access protected
+ * @return string
+ */
+ protected function serverHostname()
+ {
+ $result = 'localhost.localdomain';
+ if (!empty($this->Hostname)) {
+ $result = $this->Hostname;
+ } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
+ $result = $_SERVER['SERVER_NAME'];
+ } elseif (function_exists('gethostname') && gethostname() !== false) {
+ $result = gethostname();
+ } elseif (php_uname('n') !== false) {
+ $result = php_uname('n');
+ }
+ return $result;
+ }
+
+ /**
+ * Get an error message in the current language.
+ * @access protected
+ * @param string $key
+ * @return string
+ */
+ protected function lang($key)
+ {
+ if (count($this->language) < 1) {
+ $this->setLanguage('en'); // set the default language
+ }
+
+ if (isset($this->language[$key])) {
+ return $this->language[$key];
+ } else {
+ return 'Language string failed to load: ' . $key;
+ }
+ }
+
+ /**
+ * Check if an error occurred.
+ * @access public
+ * @return bool True if an error did occur.
+ */
+ public function isError()
+ {
+ return ($this->error_count > 0);
+ }
+
+ /**
+ * Ensure consistent line endings in a string.
+ * Changes every end of line from CRLF, CR or LF to $this->LE.
+ * @access public
+ * @param string $str String to fixEOL
+ * @return string
+ */
+ public function fixEOL($str)
+ {
+ // Normalise to \n
+ $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
+ // Now convert LE as needed
+ if ($this->LE !== "\n") {
+ $nstr = str_replace("\n", $this->LE, $nstr);
+ }
+ return $nstr;
+ }
+
+ /**
+ * Add a custom header.
+ * $name value can be overloaded to contain
+ * both header name and value (name:value)
+ * @access public
+ * @param string $name Custom header name
+ * @param string $value Header value
+ * @return void
+ */
+ public function addCustomHeader($name, $value = null)
+ {
+ if ($value === null) {
+ // Value passed in as name:value
+ $this->CustomHeader[] = explode(':', $name, 2);
+ } else {
+ $this->CustomHeader[] = array($name, $value);
+ }
+ }
+
+ /**
+ * Create a message from an HTML string.
+ * Automatically makes modifications for inline images and backgrounds
+ * and creates a plain-text version by converting the HTML.
+ * Overwrites any existing values in $this->Body and $this->AltBody
+ * @access public
+ * @param string $message HTML message string
+ * @param string $basedir baseline directory for path
+ * @param bool $advanced Whether to use the advanced HTML to text converter
+ * @return string $message
+ */
+ public function msgHTML($message, $basedir = '', $advanced = false)
+ {
+ preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
+ if (isset($images[2])) {
+ foreach ($images[2] as $imgindex => $url) {
+ // do not change urls for absolute images (thanks to corvuscorax)
+ if (!preg_match('#^[A-z]+://#', $url)) {
+ $filename = basename($url);
+ $directory = dirname($url);
+ if ($directory == '.') {
+ $directory = '';
+ }
+ $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2
+ if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
+ $basedir .= '/';
+ }
+ if (strlen($directory) > 1 && substr($directory, -1) != '/') {
+ $directory .= '/';
+ }
+ if ($this->addEmbeddedImage(
+ $basedir . $directory . $filename,
+ $cid,
+ $filename,
+ 'base64',
+ self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
+ )
+ ) {
+ $message = preg_replace(
+ '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
+ $images[1][$imgindex] . '="cid:' . $cid . '"',
+ $message
+ );
+ }
+ }
+ }
+ }
+ $this->isHTML(true);
+ //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
+ $this->Body = $this->normalizeBreaks($message);
+ $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
+ if (empty($this->AltBody)) {
+ $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
+ self::CRLF . self::CRLF;
+ }
+ return $this->Body;
+ }
+
+ /**
+ * Convert an HTML string into plain text.
+ * @param string $html The HTML text to convert
+ * @param bool $advanced Should this use the more complex html2text converter or just a simple one?
+ * @return string
+ */
+ public function html2text($html, $advanced = false)
+ {
+ if ($advanced) {
+ require_once 'extras/class.html2text.php';
+ $htmlconverter = new html2text($html);
+ return $htmlconverter->get_text();
+ }
+ return html_entity_decode(
+ trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
+ ENT_QUOTES,
+ $this->CharSet
+ );
+ }
+
+ /**
+ * Get the MIME type for a file extension.
+ * @param string $ext File extension
+ * @access public
+ * @return string MIME type of file.
+ * @static
+ */
+ public static function _mime_types($ext = '')
+ {
+ $mimes = array(
+ 'xl' => 'application/excel',
+ 'hqx' => 'application/mac-binhex40',
+ 'cpt' => 'application/mac-compactpro',
+ 'bin' => 'application/macbinary',
+ 'doc' => 'application/msword',
+ 'word' => 'application/msword',
+ 'class' => 'application/octet-stream',
+ 'dll' => 'application/octet-stream',
+ 'dms' => 'application/octet-stream',
+ 'exe' => 'application/octet-stream',
+ 'lha' => 'application/octet-stream',
+ 'lzh' => 'application/octet-stream',
+ 'psd' => 'application/octet-stream',
+ 'sea' => 'application/octet-stream',
+ 'so' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'pdf' => 'application/pdf',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'mif' => 'application/vnd.mif',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'wbxml' => 'application/vnd.wap.wbxml',
+ 'wmlc' => 'application/vnd.wap.wmlc',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'gtar' => 'application/x-gtar',
+ 'php3' => 'application/x-httpd-php',
+ 'php4' => 'application/x-httpd-php',
+ 'php' => 'application/x-httpd-php',
+ 'phtml' => 'application/x-httpd-php',
+ 'phps' => 'application/x-httpd-php-source',
+ 'js' => 'application/x-javascript',
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'tar' => 'application/x-tar',
+ 'tgz' => 'application/x-tar',
+ 'xht' => 'application/xhtml+xml',
+ 'xhtml' => 'application/xhtml+xml',
+ 'zip' => 'application/zip',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mpga' => 'audio/mpeg',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'ra' => 'audio/x-realaudio',
+ 'wav' => 'audio/x-wav',
+ 'bmp' => 'image/bmp',
+ 'gif' => 'image/gif',
+ 'jpeg' => 'image/jpeg',
+ 'jpe' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'eml' => 'message/rfc822',
+ 'css' => 'text/css',
+ 'html' => 'text/html',
+ 'htm' => 'text/html',
+ 'shtml' => 'text/html',
+ 'log' => 'text/plain',
+ 'text' => 'text/plain',
+ 'txt' => 'text/plain',
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'vcf' => 'text/vcard',
+ 'vcard' => 'text/vcard',
+ 'xml' => 'text/xml',
+ 'xsl' => 'text/xml',
+ 'mpeg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mov' => 'video/quicktime',
+ 'qt' => 'video/quicktime',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 'avi' => 'video/x-msvideo',
+ 'movie' => 'video/x-sgi-movie'
+ );
+ return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
+ }
+
+ /**
+ * Map a file name to a MIME type.
+ * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
+ * @param string $filename A file name or full path, does not need to exist as a file
+ * @return string
+ * @static
+ */
+ public static function filenameToType($filename)
+ {
+ //In case the path is a URL, strip any query string before getting extension
+ $qpos = strpos($filename, '?');
+ if ($qpos !== false) {
+ $filename = substr($filename, 0, $qpos);
+ }
+ $pathinfo = self::mb_pathinfo($filename);
+ return self::_mime_types($pathinfo['extension']);
+ }
+
+ /**
+ * Multi-byte-safe pathinfo replacement.
+ * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
+ * Works similarly to the one in PHP >= 5.2.0
+ * @link http://www.php.net/manual/en/function.pathinfo.php#107461
+ * @param string $path A filename or path, does not need to exist as a file
+ * @param integer|string $options Either a PATHINFO_* constant,
+ * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
+ * @return string|array
+ * @static
+ */
+ public static function mb_pathinfo($path, $options = null)
+ {
+ $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
+ $pathinfo = array();
+ preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo);
+ if (array_key_exists(1, $pathinfo)) {
+ $ret['dirname'] = $pathinfo[1];
+ }
+ if (array_key_exists(2, $pathinfo)) {
+ $ret['basename'] = $pathinfo[2];
+ }
+ if (array_key_exists(5, $pathinfo)) {
+ $ret['extension'] = $pathinfo[5];
+ }
+ if (array_key_exists(3, $pathinfo)) {
+ $ret['filename'] = $pathinfo[3];
+ }
+ switch ($options) {
+ case PATHINFO_DIRNAME:
+ case 'dirname':
+ return $ret['dirname'];
+ break;
+ case PATHINFO_BASENAME:
+ case 'basename':
+ return $ret['basename'];
+ break;
+ case PATHINFO_EXTENSION:
+ case 'extension':
+ return $ret['extension'];
+ break;
+ case PATHINFO_FILENAME:
+ case 'filename':
+ return $ret['filename'];
+ break;
+ default:
+ return $ret;
+ }
+ }
+
+ /**
+ * Set or reset instance properties.
+ *
+ * Usage Example:
+ * $page->set('X-Priority', '3');
+ *
+ * @access public
+ * @param string $name
+ * @param mixed $value
+ * NOTE: will not work with arrays, there are no arrays to set/reset
+ * @throws phpmailerException
+ * @return bool
+ * @todo Should this not be using __set() magic function?
+ */
+ public function set($name, $value = '')
+ {
+ try {
+ if (isset($this->$name)) {
+ $this->$name = $value;
+ } else {
+ throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
+ }
+ } catch (Exception $exc) {
+ $this->setError($exc->getMessage());
+ if ($exc->getCode() == self::STOP_CRITICAL) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Strip newlines to prevent header injection.
+ * @access public
+ * @param string $str
+ * @return string
+ */
+ public function secureHeader($str)
+ {
+ return trim(str_replace(array("\r", "\n"), '', $str));
+ }
+
+ /**
+ * Normalize line breaks in a string.
+ * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
+ * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
+ * @param string $text
+ * @param string $breaktype What kind of line break to use, defaults to CRLF
+ * @return string
+ * @access public
+ * @static
+ */
+ public static function normalizeBreaks($text, $breaktype = "\r\n")
+ {
+ return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
+ }
+
+
+ /**
+ * Set the public and private key files and password for S/MIME signing.
+ * @access public
+ * @param string $cert_filename
+ * @param string $key_filename
+ * @param string $key_pass Password for private key
+ */
+ public function sign($cert_filename, $key_filename, $key_pass)
+ {
+ $this->sign_cert_file = $cert_filename;
+ $this->sign_key_file = $key_filename;
+ $this->sign_key_pass = $key_pass;
+ }
+
+ /**
+ * Quoted-Printable-encode a DKIM header.
+ * @access public
+ * @param string $txt
+ * @return string
+ */
+ public function DKIM_QP($txt)
+ {
+ $line = '';
+ for ($i = 0; $i < strlen($txt); $i++) {
+ $ord = ord($txt[$i]);
+ if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
+ $line .= $txt[$i];
+ } else {
+ $line .= '=' . sprintf('%02X', $ord);
+ }
+ }
+ return $line;
+ }
+
+ /**
+ * Generate a DKIM signature.
+ * @access public
+ * @param string $signheader Header
+ * @throws phpmailerException
+ * @return string
+ */
+ public function DKIM_Sign($signheader)
+ {
+ if (!defined('PKCS7_TEXT')) {
+ if ($this->exceptions) {
+ throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
+ }
+ return '';
+ }
+ $privKeyStr = file_get_contents($this->DKIM_private);
+ if ($this->DKIM_passphrase != '') {
+ $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
+ } else {
+ $privKey = $privKeyStr;
+ }
+ if (openssl_sign($signheader, $signature, $privKey)) {
+ return base64_encode($signature);
+ }
+ return '';
+ }
+
+ /**
+ * Generate a DKIM canonicalization header.
+ * @access public
+ * @param string $signheader Header
+ * @return string
+ */
+ public function DKIM_HeaderC($signheader)
+ {
+ $signheader = preg_replace('/\r\n\s+/', ' ', $signheader);
+ $lines = explode("\r\n", $signheader);
+ foreach ($lines as $key => $line) {
+ list($heading, $value) = explode(':', $line, 2);
+ $heading = strtolower($heading);
+ $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
+ $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
+ }
+ $signheader = implode("\r\n", $lines);
+ return $signheader;
+ }
+
+ /**
+ * Generate a DKIM canonicalization body.
+ * @access public
+ * @param string $body Message Body
+ * @return string
+ */
+ public function DKIM_BodyC($body)
+ {
+ if ($body == '') {
+ return "\r\n";
+ }
+ // stabilize line endings
+ $body = str_replace("\r\n", "\n", $body);
+ $body = str_replace("\n", "\r\n", $body);
+ // END stabilize line endings
+ while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
+ $body = substr($body, 0, strlen($body) - 2);
+ }
+ return $body;
+ }
+
+ /**
+ * Create the DKIM header and body in a new message header.
+ * @access public
+ * @param string $headers_line Header lines
+ * @param string $subject Subject
+ * @param string $body Body
+ * @return string
+ */
+ public function DKIM_Add($headers_line, $subject, $body)
+ {
+ $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
+ $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
+ $DKIMquery = 'dns/txt'; // Query method
+ $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
+ $subject_header = "Subject: $subject";
+ $headers = explode($this->LE, $headers_line);
+ $from_header = '';
+ $to_header = '';
+ $current = '';
+ foreach ($headers as $header) {
+ if (strpos($header, 'From:') === 0) {
+ $from_header = $header;
+ $current = 'from_header';
+ } elseif (strpos($header, 'To:') === 0) {
+ $to_header = $header;
+ $current = 'to_header';
+ } else {
+ if ($current && strpos($header, ' =?') === 0) {
+ $current .= $header;
+ } else {
+ $current = '';
+ }
+ }
+ }
+ $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
+ $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
+ $subject = str_replace(
+ '|',
+ '=7C',
+ $this->DKIM_QP($subject_header)
+ ); // Copied header fields (dkim-quoted-printable)
+ $body = $this->DKIM_BodyC($body);
+ $DKIMlen = strlen($body); // Length of body
+ $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
+ $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
+ $dkimhdrs = 'DKIM-Signature: v=1; a=' .
+ $DKIMsignatureType . '; q=' .
+ $DKIMquery . '; l=' .
+ $DKIMlen . '; s=' .
+ $this->DKIM_selector .
+ ";\r\n" .
+ "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
+ "\th=From:To:Subject;\r\n" .
+ "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
+ "\tz=$from\r\n" .
+ "\t|$to\r\n" .
+ "\t|$subject;\r\n" .
+ "\tbh=" . $DKIMb64 . ";\r\n" .
+ "\tb=";
+ $toSign = $this->DKIM_HeaderC(
+ $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
+ );
+ $signed = $this->DKIM_Sign($toSign);
+ return $dkimhdrs . $signed . "\r\n";
+ }
+
+ /**
+ * Allows for public read access to 'to' property.
+ * @access public
+ * @return array
+ */
+ public function getToAddresses()
+ {
+ return $this->to;
+ }
+
+ /**
+ * Allows for public read access to 'cc' property.
+ * @access public
+ * @return array
+ */
+ public function getCcAddresses()
+ {
+ return $this->cc;
+ }
+
+ /**
+ * Allows for public read access to 'bcc' property.
+ * @access public
+ * @return array
+ */
+ public function getBccAddresses()
+ {
+ return $this->bcc;
+ }
+
+ /**
+ * Allows for public read access to 'ReplyTo' property.
+ * @access public
+ * @return array
+ */
+ public function getReplyToAddresses()
+ {
+ return $this->ReplyTo;
+ }
+
+ /**
+ * Allows for public read access to 'all_recipients' property.
+ * @access public
+ * @return array
+ */
+ public function getAllRecipientAddresses()
+ {
+ return $this->all_recipients;
+ }
+
+ /**
+ * Perform a callback.
+ * @param bool $isSent
+ * @param string $to
+ * @param string $cc
+ * @param string $bcc
+ * @param string $subject
+ * @param string $body
+ * @param string $from
+ */
+ protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null)
+ {
+ if (!empty($this->action_function) && is_callable($this->action_function)) {
+ $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
+ call_user_func_array($this->action_function, $params);
+ }
+ }
+}
+
+/**
+ * PHPMailer exception handler
+ * @package PHPMailer
+ */
+class phpmailerException extends Exception
+{
+ /**
+ * Prettify error message output
+ * @return string
+ */
+ public function errorMessage()
+ {
+ $errorMsg = '' . $this->getMessage() . " \n";
+ return $errorMsg;
+ }
+}
+
+}
diff --git a/app/lib/PHPMailerAutoload.inc b/app/lib/PHPMailerAutoload.inc
new file mode 100644
index 00000000..58186b67
--- /dev/null
+++ b/app/lib/PHPMailerAutoload.inc
@@ -0,0 +1,86 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\lib
+ {
+
+
+ /**
+ * Class to ensure that Compatibility library below is loaded.
+ *
+ * @author Oliver Hanraths
+ */
+ class PHPMailerAutoload
+ {
+
+ /**
+ * Call this function to ensure this file is loaded.
+ */
+ public static function load()
+ {
+ }
+
+ }
+
+ }
+
+ namespace {
+
+/**
+ * PHPMailer SPL autoloader.
+ * PHP Version 5
+ * @package PHPMailer
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ * @author Marcus Bointon (Synchro/coolbru)
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer SPL autoloader.
+ * @param string $classname The name of the class to load
+ */
+function PHPMailerAutoload($classname)
+{
+ //Can't use __DIR__ as it's only in PHP 5.3+
+ $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php';
+ if (is_readable($filename)) {
+ require $filename;
+ }
+}
+
+if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
+ //SPL autoloading was introduced in PHP 5.1.2
+ if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
+ spl_autoload_register('PHPMailerAutoload', true, true);
+ } else {
+ spl_autoload_register('PHPMailerAutoload');
+ }
+} else {
+ /**
+ * Fall back to traditional autoload for old PHP versions
+ * @param string $classname The name of the class to load
+ */
+ function __autoload($classname)
+ {
+ PHPMailerAutoload($classname);
+ }
+}
+
+}
diff --git a/app/lib/Password.inc b/app/lib/Password.inc
new file mode 100644
index 00000000..e038403a
--- /dev/null
+++ b/app/lib/Password.inc
@@ -0,0 +1,316 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\lib
+ {
+
+
+ /**
+ * Class to ensure that Compatibility library below is loaded.
+ *
+ * @author Oliver Hanraths
+ */
+ class Password
+ {
+
+ /**
+ * Call this function to ensure this file is loaded.
+ */
+ public static function load()
+ {
+ }
+
+ }
+
+ }
+
+
+
+
+/**
+ * A Compatibility library with PHP 5.5's simplified password hashing API.
+ *
+ * @author Anthony Ferrara
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @copyright 2012 The Authors
+ */
+
+namespace {
+
+if (!defined('PASSWORD_DEFAULT')) {
+
+ define('PASSWORD_BCRYPT', 1);
+ define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
+
+ /**
+ * Hash the password using the specified algorithm
+ *
+ * @param string $password The password to hash
+ * @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
+ * @param array $options The options for the algorithm to use
+ *
+ * @return string|false The hashed password, or false on error.
+ */
+ function password_hash($password, $algo, array $options = array()) {
+ if (!function_exists('crypt')) {
+ trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
+ return null;
+ }
+ if (!is_string($password)) {
+ trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
+ return null;
+ }
+ if (!is_int($algo)) {
+ trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
+ return null;
+ }
+ $resultLength = 0;
+ switch ($algo) {
+ case PASSWORD_BCRYPT:
+ // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
+ $cost = 10;
+ if (isset($options['cost'])) {
+ $cost = $options['cost'];
+ if ($cost < 4 || $cost > 31) {
+ trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
+ return null;
+ }
+ }
+ // The length of salt to generate
+ $raw_salt_len = 16;
+ // The length required in the final serialization
+ $required_salt_len = 22;
+ $hash_format = sprintf("$2y$%02d$", $cost);
+ // The expected length of the final crypt() output
+ $resultLength = 60;
+ break;
+ default:
+ trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
+ return null;
+ }
+ $salt_requires_encoding = false;
+ if (isset($options['salt'])) {
+ switch (gettype($options['salt'])) {
+ case 'NULL':
+ case 'boolean':
+ case 'integer':
+ case 'double':
+ case 'string':
+ $salt = (string) $options['salt'];
+ break;
+ case 'object':
+ if (method_exists($options['salt'], '__tostring')) {
+ $salt = (string) $options['salt'];
+ break;
+ }
+ case 'array':
+ case 'resource':
+ default:
+ trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
+ return null;
+ }
+ if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
+ trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
+ return null;
+ } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
+ $salt_requires_encoding = true;
+ }
+ } else {
+ $buffer = '';
+ $buffer_valid = false;
+ if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
+ $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
+ if ($buffer) {
+ $buffer_valid = true;
+ }
+ }
+ if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
+ $buffer = openssl_random_pseudo_bytes($raw_salt_len);
+ if ($buffer) {
+ $buffer_valid = true;
+ }
+ }
+ if (!$buffer_valid && @is_readable('/dev/urandom')) {
+ $f = fopen('/dev/urandom', 'r');
+ $read = PasswordCompat\binary\_strlen($buffer);
+ while ($read < $raw_salt_len) {
+ $buffer .= fread($f, $raw_salt_len - $read);
+ $read = PasswordCompat\binary\_strlen($buffer);
+ }
+ fclose($f);
+ if ($read >= $raw_salt_len) {
+ $buffer_valid = true;
+ }
+ }
+ if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
+ $bl = PasswordCompat\binary\_strlen($buffer);
+ for ($i = 0; $i < $raw_salt_len; $i++) {
+ if ($i < $bl) {
+ $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
+ } else {
+ $buffer .= chr(mt_rand(0, 255));
+ }
+ }
+ }
+ $salt = $buffer;
+ $salt_requires_encoding = true;
+ }
+ if ($salt_requires_encoding) {
+ // encode string with the Base64 variant used by crypt
+ $base64_digits =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ $bcrypt64_digits =
+ './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+ $base64_string = base64_encode($salt);
+ $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
+ }
+ $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
+
+ $hash = $hash_format . $salt;
+
+ $ret = crypt($password, $hash);
+
+ if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
+ return false;
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Get information about the password hash. Returns an array of the information
+ * that was used to generate the password hash.
+ *
+ * array(
+ * 'algo' => 1,
+ * 'algoName' => 'bcrypt',
+ * 'options' => array(
+ * 'cost' => 10,
+ * ),
+ * )
+ *
+ * @param string $hash The password hash to extract info from
+ *
+ * @return array The array of information about the hash.
+ */
+ function password_get_info($hash) {
+ $return = array(
+ 'algo' => 0,
+ 'algoName' => 'unknown',
+ 'options' => array(),
+ );
+ if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
+ $return['algo'] = PASSWORD_BCRYPT;
+ $return['algoName'] = 'bcrypt';
+ list($cost) = sscanf($hash, "$2y$%d$");
+ $return['options']['cost'] = $cost;
+ }
+ return $return;
+ }
+
+ /**
+ * Determine if the password hash needs to be rehashed according to the options provided
+ *
+ * If the answer is true, after validating the password using password_verify, rehash it.
+ *
+ * @param string $hash The hash to test
+ * @param int $algo The algorithm used for new password hashes
+ * @param array $options The options array passed to password_hash
+ *
+ * @return boolean True if the password needs to be rehashed.
+ */
+ function password_needs_rehash($hash, $algo, array $options = array()) {
+ $info = password_get_info($hash);
+ if ($info['algo'] != $algo) {
+ return true;
+ }
+ switch ($algo) {
+ case PASSWORD_BCRYPT:
+ $cost = isset($options['cost']) ? $options['cost'] : 10;
+ if ($cost != $info['options']['cost']) {
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Verify a password against a hash using a timing attack resistant approach
+ *
+ * @param string $password The password to verify
+ * @param string $hash The hash to verify against
+ *
+ * @return boolean If the password matches the hash
+ */
+ function password_verify($password, $hash) {
+ if (!function_exists('crypt')) {
+ trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
+ return false;
+ }
+ $ret = crypt($password, $hash);
+ if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
+ return false;
+ }
+
+ $status = 0;
+ for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
+ $status |= (ord($ret[$i]) ^ ord($hash[$i]));
+ }
+
+ return $status === 0;
+ }
+}
+
+}
+
+namespace PasswordCompat\binary {
+ /**
+ * Count the number of bytes in a string
+ *
+ * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.
+ * In this case, strlen() will count the number of *characters* based on the internal encoding. A
+ * sequence of bytes might be regarded as a single multibyte character.
+ *
+ * @param string $binary_string The input string
+ *
+ * @internal
+ * @return int The number of bytes
+ */
+ function _strlen($binary_string) {
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($binary_string, '8bit');
+ }
+ return strlen($binary_string);
+ }
+
+ /**
+ * Get a substring based on byte limits
+ *
+ * @see _strlen()
+ *
+ * @param string $binary_string The input string
+ * @param int $start
+ * @param int $length
+ *
+ * @internal
+ * @return string The substring
+ */
+ function _substr($binary_string, $start, $length) {
+ if (function_exists('mb_substr')) {
+ return mb_substr($binary_string, $start, $length, '8bit');
+ }
+ return substr($binary_string, $start, $length);
+ }
+
+}
diff --git a/app/lib/SMTP.inc b/app/lib/SMTP.inc
new file mode 100644
index 00000000..18d2ee10
--- /dev/null
+++ b/app/lib/SMTP.inc
@@ -0,0 +1,972 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\lib
+ {
+
+
+ /**
+ * Class to ensure that Compatibility library below is loaded.
+ *
+ * @author Oliver Hanraths
+ */
+ class SMTP
+ {
+
+ /**
+ * Call this function to ensure this file is loaded.
+ */
+ public static function load()
+ {
+ }
+
+ }
+
+ }
+
+ namespace {
+
+/**
+ * PHPMailer RFC821 SMTP email transport class.
+ * PHP Version 5
+ * @package PHPMailer
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ * @author Marcus Bointon (Synchro/coolbru)
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer RFC821 SMTP email transport class.
+ * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
+ * @package PHPMailer
+ * @author Chris Ryan
+ * @author Marcus Bointon
+ */
+class SMTP
+{
+ /**
+ * The PHPMailer SMTP version number.
+ * @type string
+ */
+ const VERSION = '5.2.8';
+
+ /**
+ * SMTP line break constant.
+ * @type string
+ */
+ const CRLF = "\r\n";
+
+ /**
+ * The SMTP port to use if one is not specified.
+ * @type int
+ */
+ const DEFAULT_SMTP_PORT = 25;
+
+ /**
+ * The maximum line length allowed by RFC 2822 section 2.1.1
+ * @type int
+ */
+ const MAX_LINE_LENGTH = 998;
+
+ /**
+ * The PHPMailer SMTP Version number.
+ * @type string
+ * @deprecated Use the constant instead
+ * @see SMTP::VERSION
+ */
+ public $Version = '5.2.8';
+
+ /**
+ * SMTP server port number.
+ * @type int
+ * @deprecated This is only ever used as a default value, so use the constant instead
+ * @see SMTP::DEFAULT_SMTP_PORT
+ */
+ public $SMTP_PORT = 25;
+
+ /**
+ * SMTP reply line ending.
+ * @type string
+ * @deprecated Use the constant instead
+ * @see SMTP::CRLF
+ */
+ public $CRLF = "\r\n";
+
+ /**
+ * Debug output level.
+ * Options:
+ * * `0` No output
+ * * `1` Commands
+ * * `2` Data and commands
+ * * `3` As 2 plus connection status
+ * * `4` Low-level data output
+ * @type int
+ */
+ public $do_debug = 0;
+
+ /**
+ * How to handle debug output.
+ * Options:
+ * * `echo` Output plain-text as-is, appropriate for CLI
+ * * `html` Output escaped, line breaks converted to , appropriate for browser output
+ * * `error_log` Output to error log as configured in php.ini
+ * @type string
+ */
+ public $Debugoutput = 'echo';
+
+ /**
+ * Whether to use VERP.
+ * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Info on VERP
+ * @type bool
+ */
+ public $do_verp = false;
+
+ /**
+ * The timeout value for connection, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
+ * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
+ * @type int
+ */
+ public $Timeout = 300;
+
+ /**
+ * The SMTP timelimit value for reads, in seconds.
+ * @type int
+ */
+ public $Timelimit = 30;
+
+ /**
+ * The socket for the server connection.
+ * @type resource
+ */
+ protected $smtp_conn;
+
+ /**
+ * Error message, if any, for the last call.
+ * @type string
+ */
+ protected $error = '';
+
+ /**
+ * The reply the server sent to us for HELO.
+ * @type string
+ */
+ protected $helo_rply = '';
+
+ /**
+ * The most recent reply received from the server.
+ * @type string
+ */
+ protected $last_reply = '';
+
+ /**
+ * Constructor.
+ * @access public
+ */
+ public function __construct()
+ {
+ $this->smtp_conn = 0;
+ $this->error = null;
+ $this->helo_rply = null;
+ $this->do_debug = 0;
+ }
+
+ /**
+ * Output debugging info via a user-selected method.
+ * @param string $str Debug string to output
+ * @return void
+ */
+ protected function edebug($str)
+ {
+ switch ($this->Debugoutput) {
+ case 'error_log':
+ //Don't output, just log
+ error_log($str);
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking, HTML-safe output
+ echo htmlentities(
+ preg_replace('/[\r\n]+/', '', $str),
+ ENT_QUOTES,
+ 'UTF-8'
+ )
+ . " \n";
+ break;
+ case 'echo':
+ default:
+ echo gmdate('Y-m-d H:i:s')."\t".trim($str)."\n";
+ }
+ }
+
+ /**
+ * Connect to an SMTP server.
+ * @param string $host SMTP server IP or host name
+ * @param int $port The port number to connect to
+ * @param int $timeout How long to wait for the connection to open
+ * @param array $options An array of options for stream_context_create()
+ * @access public
+ * @return bool
+ */
+ public function connect($host, $port = null, $timeout = 30, $options = array())
+ {
+ // Clear errors to avoid confusion
+ $this->error = null;
+ // Make sure we are __not__ connected
+ if ($this->connected()) {
+ // Already connected, generate error
+ $this->error = array('error' => 'Already connected to a server');
+ return false;
+ }
+ if (empty($port)) {
+ $port = self::DEFAULT_SMTP_PORT;
+ }
+ // Connect to the SMTP server
+ if ($this->do_debug >= 3) {
+ $this->edebug('Connection: opening');
+ }
+ $errno = 0;
+ $errstr = '';
+ $socket_context = stream_context_create($options);
+ //Suppress errors; connection failures are handled at a higher level
+ $this->smtp_conn = @stream_socket_client(
+ $host . ":" . $port,
+ $errno,
+ $errstr,
+ $timeout,
+ STREAM_CLIENT_CONNECT,
+ $socket_context
+ );
+ // Verify we connected properly
+ if (empty($this->smtp_conn)) {
+ $this->error = array(
+ 'error' => 'Failed to connect to server',
+ 'errno' => $errno,
+ 'errstr' => $errstr
+ );
+ if ($this->do_debug >= 1) {
+ $this->edebug(
+ 'SMTP ERROR: ' . $this->error['error']
+ . ": $errstr ($errno)"
+ );
+ }
+ return false;
+ }
+ if ($this->do_debug >= 3) {
+ $this->edebug('Connection: opened');
+ }
+ // SMTP server can take longer to respond, give longer timeout for first read
+ // Windows does not have support for this timeout function
+ if (substr(PHP_OS, 0, 3) != 'WIN') {
+ $max = ini_get('max_execution_time');
+ if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
+ @set_time_limit($timeout);
+ }
+ stream_set_timeout($this->smtp_conn, $timeout, 0);
+ }
+ // Get any announcement
+ $announce = $this->get_lines();
+ if ($this->do_debug >= 2) {
+ $this->edebug('SERVER -> CLIENT: ' . $announce);
+ }
+ return true;
+ }
+
+ /**
+ * Initiate a TLS (encrypted) session.
+ * @access public
+ * @return bool
+ */
+ public function startTLS()
+ {
+ if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
+ return false;
+ }
+ // Begin encrypted connection
+ if (!stream_socket_enable_crypto(
+ $this->smtp_conn,
+ true,
+ STREAM_CRYPTO_METHOD_TLS_CLIENT
+ )) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Perform SMTP authentication.
+ * Must be run after hello().
+ * @see hello()
+ * @param string $username The user name
+ * @param string $password The password
+ * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
+ * @param string $realm The auth realm for NTLM
+ * @param string $workstation The auth workstation for NTLM
+ * @access public
+ * @return bool True if successfully authenticated.
+ */
+ public function authenticate(
+ $username,
+ $password,
+ $authtype = 'LOGIN',
+ $realm = '',
+ $workstation = ''
+ ) {
+ if (empty($authtype)) {
+ $authtype = 'LOGIN';
+ }
+ switch ($authtype) {
+ case 'PLAIN':
+ // Start authentication
+ if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
+ return false;
+ }
+ // Send encoded username and password
+ if (!$this->sendCommand(
+ 'User & Password',
+ base64_encode("\0" . $username . "\0" . $password),
+ 235
+ )
+ ) {
+ return false;
+ }
+ break;
+ case 'LOGIN':
+ // Start authentication
+ if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
+ return false;
+ }
+ if (!$this->sendCommand("Username", base64_encode($username), 334)) {
+ return false;
+ }
+ if (!$this->sendCommand("Password", base64_encode($password), 235)) {
+ return false;
+ }
+ break;
+ case 'NTLM':
+ /*
+ * ntlm_sasl_client.php
+ * Bundled with Permission
+ *
+ * How to telnet in windows:
+ * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
+ * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
+ */
+ require_once 'extras/ntlm_sasl_client.php';
+ $temp = new stdClass();
+ $ntlm_client = new ntlm_sasl_client_class;
+ //Check that functions are available
+ if (!$ntlm_client->Initialize($temp)) {
+ $this->error = array('error' => $temp->error);
+ if ($this->do_debug >= 1) {
+ $this->edebug(
+ 'You need to enable some modules in your php.ini file: '
+ . $this->error['error']
+ );
+ }
+ return false;
+ }
+ //msg1
+ $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
+
+ if (!$this->sendCommand(
+ 'AUTH NTLM',
+ 'AUTH NTLM ' . base64_encode($msg1),
+ 334
+ )
+ ) {
+ return false;
+ }
+ //Though 0 based, there is a white space after the 3 digit number
+ //msg2
+ $challenge = substr($this->last_reply, 3);
+ $challenge = base64_decode($challenge);
+ $ntlm_res = $ntlm_client->NTLMResponse(
+ substr($challenge, 24, 8),
+ $password
+ );
+ //msg3
+ $msg3 = $ntlm_client->TypeMsg3(
+ $ntlm_res,
+ $username,
+ $realm,
+ $workstation
+ );
+ // send encoded username
+ return $this->sendCommand('Username', base64_encode($msg3), 235);
+ break;
+ case 'CRAM-MD5':
+ // Start authentication
+ if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
+ return false;
+ }
+ // Get the challenge
+ $challenge = base64_decode(substr($this->last_reply, 4));
+
+ // Build the response
+ $response = $username . ' ' . $this->hmac($challenge, $password);
+
+ // send encoded credentials
+ return $this->sendCommand('Username', base64_encode($response), 235);
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Calculate an MD5 HMAC hash.
+ * Works like hash_hmac('md5', $data, $key)
+ * in case that function is not available
+ * @param string $data The data to hash
+ * @param string $key The key to hash with
+ * @access protected
+ * @return string
+ */
+ protected function hmac($data, $key)
+ {
+ if (function_exists('hash_hmac')) {
+ return hash_hmac('md5', $data, $key);
+ }
+
+ // The following borrowed from
+ // http://php.net/manual/en/function.mhash.php#27225
+
+ // RFC 2104 HMAC implementation for php.
+ // Creates an md5 HMAC.
+ // Eliminates the need to install mhash to compute a HMAC
+ // Hacked by Lance Rushing
+
+ $bytelen = 64; // byte length for md5
+ if (strlen($key) > $bytelen) {
+ $key = pack('H*', md5($key));
+ }
+ $key = str_pad($key, $bytelen, chr(0x00));
+ $ipad = str_pad('', $bytelen, chr(0x36));
+ $opad = str_pad('', $bytelen, chr(0x5c));
+ $k_ipad = $key ^ $ipad;
+ $k_opad = $key ^ $opad;
+
+ return md5($k_opad . pack('H*', md5($k_ipad . $data)));
+ }
+
+ /**
+ * Check connection state.
+ * @access public
+ * @return bool True if connected.
+ */
+ public function connected()
+ {
+ if (!empty($this->smtp_conn)) {
+ $sock_status = stream_get_meta_data($this->smtp_conn);
+ if ($sock_status['eof']) {
+ // the socket is valid but we are not connected
+ if ($this->do_debug >= 1) {
+ $this->edebug(
+ 'SMTP NOTICE: EOF caught while checking if connected'
+ );
+ }
+ $this->close();
+ return false;
+ }
+ return true; // everything looks good
+ }
+ return false;
+ }
+
+ /**
+ * Close the socket and clean up the state of the class.
+ * Don't use this function without first trying to use QUIT.
+ * @see quit()
+ * @access public
+ * @return void
+ */
+ public function close()
+ {
+ $this->error = null; // so there is no confusion
+ $this->helo_rply = null;
+ if (!empty($this->smtp_conn)) {
+ // close the connection and cleanup
+ fclose($this->smtp_conn);
+ if ($this->do_debug >= 3) {
+ $this->edebug('Connection: closed');
+ }
+ $this->smtp_conn = 0;
+ }
+ }
+
+ /**
+ * Send an SMTP DATA command.
+ * Issues a data command and sends the msg_data to the server,
+ * finializing the mail transaction. $msg_data is the message
+ * that is to be send with the headers. Each header needs to be
+ * on a single line followed by a with the message headers
+ * and the message body being separated by and additional .
+ * Implements rfc 821: DATA
+ * @param string $msg_data Message data to send
+ * @access public
+ * @return bool
+ */
+ public function data($msg_data)
+ {
+ if (!$this->sendCommand('DATA', 'DATA', 354)) {
+ return false;
+ }
+ /* The server is ready to accept data!
+ * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
+ * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
+ * smaller lines to fit within the limit.
+ * We will also look for lines that start with a '.' and prepend an additional '.'.
+ * NOTE: this does not count towards line-length limit.
+ */
+
+ // Normalize line breaks before exploding
+ $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
+
+ /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
+ * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
+ * process all lines before a blank line as headers.
+ */
+
+ $field = substr($lines[0], 0, strpos($lines[0], ':'));
+ $in_headers = false;
+ if (!empty($field) && strpos($field, ' ') === false) {
+ $in_headers = true;
+ }
+
+ foreach ($lines as $line) {
+ $lines_out = array();
+ if ($in_headers and $line == '') {
+ $in_headers = false;
+ }
+ // ok we need to break this line up into several smaller lines
+ //This is a small micro-optimisation: isset($str[$len]) is equivalent to (strlen($str) > $len)
+ while (isset($line[self::MAX_LINE_LENGTH])) {
+ //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
+ //so as to avoid breaking in the middle of a word
+ $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
+ if (!$pos) { //Deliberately matches both false and 0
+ //No nice break found, add a hard break
+ $pos = self::MAX_LINE_LENGTH - 1;
+ $lines_out[] = substr($line, 0, $pos);
+ $line = substr($line, $pos);
+ } else {
+ //Break at the found point
+ $lines_out[] = substr($line, 0, $pos);
+ //Move along by the amount we dealt with
+ $line = substr($line, $pos + 1);
+ }
+ /* If processing headers add a LWSP-char to the front of new line
+ * RFC822 section 3.1.1
+ */
+ if ($in_headers) {
+ $line = "\t" . $line;
+ }
+ }
+ $lines_out[] = $line;
+
+ // Send the lines to the server
+ foreach ($lines_out as $line_out) {
+ //RFC2821 section 4.5.2
+ if (!empty($line_out) and $line_out[0] == '.') {
+ $line_out = '.' . $line_out;
+ }
+ $this->client_send($line_out . self::CRLF);
+ }
+ }
+
+ // Message data has been sent, complete the command
+ return $this->sendCommand('DATA END', '.', 250);
+ }
+
+ /**
+ * Send an SMTP HELO or EHLO command.
+ * Used to identify the sending server to the receiving server.
+ * This makes sure that client and server are in a known state.
+ * Implements RFC 821: HELO
+ * and RFC 2821 EHLO.
+ * @param string $host The host name or IP to connect to
+ * @access public
+ * @return bool
+ */
+ public function hello($host = '')
+ {
+ // Try extended hello first (RFC 2821)
+ return (bool)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
+ }
+
+ /**
+ * Send an SMTP HELO or EHLO command.
+ * Low-level implementation used by hello()
+ * @see hello()
+ * @param string $hello The HELO string
+ * @param string $host The hostname to say we are
+ * @access protected
+ * @return bool
+ */
+ protected function sendHello($hello, $host)
+ {
+ $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
+ $this->helo_rply = $this->last_reply;
+ return $noerror;
+ }
+
+ /**
+ * Send an SMTP MAIL command.
+ * Starts a mail transaction from the email address specified in
+ * $from. Returns true if successful or false otherwise. If True
+ * the mail transaction is started and then one or more recipient
+ * commands may be called followed by a data command.
+ * Implements rfc 821: MAIL FROM:
+ * @param string $from Source address of this message
+ * @access public
+ * @return bool
+ */
+ public function mail($from)
+ {
+ $useVerp = ($this->do_verp ? ' XVERP' : '');
+ return $this->sendCommand(
+ 'MAIL FROM',
+ 'MAIL FROM:<' . $from . '>' . $useVerp,
+ 250
+ );
+ }
+
+ /**
+ * Send an SMTP QUIT command.
+ * Closes the socket if there is no error or the $close_on_error argument is true.
+ * Implements from rfc 821: QUIT
+ * @param bool $close_on_error Should the connection close if an error occurs?
+ * @access public
+ * @return bool
+ */
+ public function quit($close_on_error = true)
+ {
+ $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
+ $err = $this->error; //Save any error
+ if ($noerror or $close_on_error) {
+ $this->close();
+ $this->error = $err; //Restore any error from the quit command
+ }
+ return $noerror;
+ }
+
+ /**
+ * Send an SMTP RCPT command.
+ * Sets the TO argument to $toaddr.
+ * Returns true if the recipient was accepted false if it was rejected.
+ * Implements from rfc 821: RCPT TO:
+ * @param string $toaddr The address the message is being sent to
+ * @access public
+ * @return bool
+ */
+ public function recipient($toaddr)
+ {
+ return $this->sendCommand(
+ 'RCPT TO',
+ 'RCPT TO:<' . $toaddr . '>',
+ array(250, 251)
+ );
+ }
+
+ /**
+ * Send an SMTP RSET command.
+ * Abort any transaction that is currently in progress.
+ * Implements rfc 821: RSET
+ * @access public
+ * @return bool True on success.
+ */
+ public function reset()
+ {
+ return $this->sendCommand('RSET', 'RSET', 250);
+ }
+
+ /**
+ * Send a command to an SMTP server and check its return code.
+ * @param string $command The command name - not sent to the server
+ * @param string $commandstring The actual command to send
+ * @param int|array $expect One or more expected integer success codes
+ * @access protected
+ * @return bool True on success.
+ */
+ protected function sendCommand($command, $commandstring, $expect)
+ {
+ if (!$this->connected()) {
+ $this->error = array(
+ 'error' => "Called $command without being connected"
+ );
+ return false;
+ }
+ $this->client_send($commandstring . self::CRLF);
+
+ $reply = $this->get_lines();
+ $code = substr($reply, 0, 3);
+
+ if ($this->do_debug >= 2) {
+ $this->edebug('SERVER -> CLIENT: ' . $reply);
+ }
+
+ if (!in_array($code, (array)$expect)) {
+ $this->last_reply = null;
+ $this->error = array(
+ 'error' => "$command command failed",
+ 'smtp_code' => $code,
+ 'detail' => substr($reply, 4)
+ );
+ if ($this->do_debug >= 1) {
+ $this->edebug(
+ 'SMTP ERROR: ' . $this->error['error'] . ': ' . $reply
+ );
+ }
+ return false;
+ }
+
+ $this->last_reply = $reply;
+ $this->error = null;
+ return true;
+ }
+
+ /**
+ * Send an SMTP SAML command.
+ * Starts a mail transaction from the email address specified in $from.
+ * Returns true if successful or false otherwise. If True
+ * the mail transaction is started and then one or more recipient
+ * commands may be called followed by a data command. This command
+ * will send the message to the users terminal if they are logged
+ * in and send them an email.
+ * Implements rfc 821: SAML FROM:
+ * @param string $from The address the message is from
+ * @access public
+ * @return bool
+ */
+ public function sendAndMail($from)
+ {
+ return $this->sendCommand('SAML', "SAML FROM:$from", 250);
+ }
+
+ /**
+ * Send an SMTP VRFY command.
+ * @param string $name The name to verify
+ * @access public
+ * @return bool
+ */
+ public function verify($name)
+ {
+ return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
+ }
+
+ /**
+ * Send an SMTP NOOP command.
+ * Used to keep keep-alives alive, doesn't actually do anything
+ * @access public
+ * @return bool
+ */
+ public function noop()
+ {
+ return $this->sendCommand('NOOP', 'NOOP', 250);
+ }
+
+ /**
+ * Send an SMTP TURN command.
+ * This is an optional command for SMTP that this class does not support.
+ * This method is here to make the RFC821 Definition complete for this class
+ * and _may_ be implemented in future
+ * Implements from rfc 821: TURN
+ * @access public
+ * @return bool
+ */
+ public function turn()
+ {
+ $this->error = array(
+ 'error' => 'The SMTP TURN command is not implemented'
+ );
+ if ($this->do_debug >= 1) {
+ $this->edebug('SMTP NOTICE: ' . $this->error['error']);
+ }
+ return false;
+ }
+
+ /**
+ * Send raw data to the server.
+ * @param string $data The data to send
+ * @access public
+ * @return int|bool The number of bytes sent to the server or false on error
+ */
+ public function client_send($data)
+ {
+ if ($this->do_debug >= 1) {
+ $this->edebug("CLIENT -> SERVER: $data");
+ }
+ return fwrite($this->smtp_conn, $data);
+ }
+
+ /**
+ * Get the latest error.
+ * @access public
+ * @return array
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Get the last reply from the server.
+ * @access public
+ * @return string
+ */
+ public function getLastReply()
+ {
+ return $this->last_reply;
+ }
+
+ /**
+ * Read the SMTP server's response.
+ * Either before eof or socket timeout occurs on the operation.
+ * With SMTP we can tell if we have more lines to read if the
+ * 4th character is '-' symbol. If it is a space then we don't
+ * need to read anything else.
+ * @access protected
+ * @return string
+ */
+ protected function get_lines()
+ {
+ // If the connection is bad, give up straight away
+ if (!is_resource($this->smtp_conn)) {
+ return '';
+ }
+ $data = '';
+ $endtime = 0;
+ stream_set_timeout($this->smtp_conn, $this->Timeout);
+ if ($this->Timelimit > 0) {
+ $endtime = time() + $this->Timelimit;
+ }
+ while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
+ $str = @fgets($this->smtp_conn, 515);
+ if ($this->do_debug >= 4) {
+ $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
+ $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
+ }
+ $data .= $str;
+ if ($this->do_debug >= 4) {
+ $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
+ }
+ // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
+ if ((isset($str[3]) and $str[3] == ' ')) {
+ break;
+ }
+ // Timed-out? Log and break
+ $info = stream_get_meta_data($this->smtp_conn);
+ if ($info['timed_out']) {
+ if ($this->do_debug >= 4) {
+ $this->edebug(
+ 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)'
+ );
+ }
+ break;
+ }
+ // Now check if reads took too long
+ if ($endtime and time() > $endtime) {
+ if ($this->do_debug >= 4) {
+ $this->edebug(
+ 'SMTP -> get_lines(): timelimit reached ('.
+ $this->Timelimit . ' sec)'
+ );
+ }
+ break;
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Enable or disable VERP address generation.
+ * @param bool $enabled
+ */
+ public function setVerp($enabled = false)
+ {
+ $this->do_verp = $enabled;
+ }
+
+ /**
+ * Get VERP address generation mode.
+ * @return bool
+ */
+ public function getVerp()
+ {
+ return $this->do_verp;
+ }
+
+ /**
+ * Set debug output method.
+ * @param string $method The function/method to use for debugging output.
+ */
+ public function setDebugOutput($method = 'echo')
+ {
+ $this->Debugoutput = $method;
+ }
+
+ /**
+ * Get debug output method.
+ * @return string
+ */
+ public function getDebugOutput()
+ {
+ return $this->Debugoutput;
+ }
+
+ /**
+ * Set debug output level.
+ * @param int $level
+ */
+ public function setDebugLevel($level = 0)
+ {
+ $this->do_debug = $level;
+ }
+
+ /**
+ * Get debug output level.
+ * @return int
+ */
+ public function getDebugLevel()
+ {
+ return $this->do_debug;
+ }
+
+ /**
+ * Set SMTP timeout.
+ * @param int $timeout
+ */
+ public function setTimeout($timeout = 0)
+ {
+ $this->Timeout = $timeout;
+ }
+
+ /**
+ * Get SMTP timeout.
+ * @return int
+ */
+ public function getTimeout()
+ {
+ return $this->Timeout;
+ }
+}
+
+}
diff --git a/app/lib/Textile.inc b/app/lib/Textile.inc
new file mode 100644
index 00000000..b2033048
--- /dev/null
+++ b/app/lib/Textile.inc
@@ -0,0 +1,36 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\lib;
+
+
+ /**
+ * Class to ensure that Compatibility library below is loaded.
+ *
+ * @author Oliver Hanraths
+ */
+ class Textile
+ {
+
+ /**
+ * Call this function to load necessary files.
+ */
+ public static function load()
+ {
+ $path = implode(DS, array('Netcarver', 'Textile'));
+ require_once($path.DS.'DataBag.php');
+ require_once($path.DS.'Parser.php');
+ require_once($path.DS.'Tag.php');
+ }
+
+ }
+
+?>
diff --git a/app/models/QuesttypeModel.inc b/app/models/QuesttypeModel.inc
new file mode 100644
index 00000000..57f51c30
--- /dev/null
+++ b/app/models/QuesttypeModel.inc
@@ -0,0 +1,173 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\models;
+
+
+ /**
+ * Abstract class for implementing a QuesttypeModel.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class QuesttypeModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Copy a Quest.
+ *
+ * @param int $userId ID of creating user
+ * @param int $sourceQuestId ID of Quest to copy from
+ * @param int $targetQuestId ID of Quest to copy to
+ * @param int $seminaryMediaIds Mapping of SeminaryMedia-IDs from source Seminary to targetSeminary
+ */
+ public abstract function copyQuest($userId, $sourceQuestId, $targetQuestId, $seminaryMediaIds);
+
+
+ /**
+ * Delete a Quest.
+ *
+ * @param int $questId ID of Quest to delete
+ */
+ public abstract function deleteQuest($questId);
+
+
+ /**
+ * Load a Model.
+ *
+ * @static
+ * @throws \hhu\z\exceptions\QuesttypeModelNotFoundException
+ * @throws \hhu\z\exceptions\QuesttypeModelNotValidException
+ * @param string $modelName Name of the QuesttypeModel to load
+ */
+ public static function load($modelName)
+ {
+ // Determine full classname
+ $className = self::getClassName($modelName);
+
+ try {
+ // Load class
+ static::loadClass($modelName, $className);
+
+ // Validate class
+ static::checkClass($className, get_class());
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \hhu\z\exceptions\QuesttypeModelNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \hhu\z\exceptions\QuesttypeModelNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a QuesttypeModel (Factory Pattern).
+ *
+ * @static
+ * @param string $questtypeName Name of the QuesttypeModel to instantiate
+ */
+ public static function factory($questtypeName)
+ {
+ // Determine full classname
+ $className = self::getClassName($questtypeName);
+
+ // Construct and return Model
+ return new $className();
+ }
+
+
+ /**
+ * Determine the Model-classname for the given Questtype-name.
+ *
+ * @static
+ * @param string $questtypeName Questtype-name to get Model-classname of
+ * @return string Classname for the Questtype-name
+ */
+ private static function getClassName($questtypeName)
+ {
+ $className = \nre\core\ClassLoader::concatClassNames($questtypeName, \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class())), 'model');
+
+
+ return \nre\configs\AppConfig::$app['namespace']."questtypes\\$className";
+ }
+
+
+ /**
+ * Load the class of a QuesttypeModel.
+ *
+ * @static
+ * @throws \nre\exceptions\ClassNotFoundException
+ * @param string $questtypeName Name of the QuesttypeModel to load
+ * @param string $fullClassName Name of the class to load
+ */
+ private static function loadClass($questtypeName, $fullClassName)
+ {
+ // Determine folder to look in
+ $className = explode('\\', $fullClassName);
+ $className = array_pop($className);
+
+ // Determine filename
+ $fileName = ROOT.DS.\nre\configs\AppConfig::$dirs['questtypes'].DS.strtolower($questtypeName).DS.$className.\nre\configs\CoreConfig::getFileExt('includes');
+
+ // Check file
+ if(!file_exists($fileName))
+ {
+ throw new \nre\exceptions\ClassNotFoundException(
+ $fullClassName
+ );
+ }
+
+ // Include file
+ include_once($fileName);
+ }
+
+
+ /**
+ * Check inheritance of the QuesttypeModel-class.
+ *
+ * @static
+ * @throws \nre\exceptions\ClassNotValidException
+ * @param string $className Name of the class to check
+ * @param string $parentClassName Name of the parent class
+ */
+ public static function checkClass($className, $parentClassName)
+ {
+ // Check if class is subclass of parent class
+ if(!is_subclass_of($className, $parentClassName)) {
+ throw new \nre\exceptions\ClassNotValidException(
+ $className
+ );
+ }
+ }
+
+
+
+
+ /**
+ * Construct a new QuesttypeModel.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \hhu\z\exceptions\QuesttypeModelNotValidException
+ * @throws \hhu\z\exceptions\QuesttypeModelNotFoundException
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ }
+
+?>
diff --git a/app/requests/MailRequest.inc b/app/requests/MailRequest.inc
new file mode 100644
index 00000000..c15c0842
--- /dev/null
+++ b/app/requests/MailRequest.inc
@@ -0,0 +1,92 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\requests;
+
+
+ /**
+ * Representation of a mail-request.
+ *
+ * @author Oliver Hanraths
+ */
+ class MailRequest extends \nre\core\Request
+ {
+ /**
+ * Linker
+ *
+ * @var \nre\core\Linker
+ */
+ private $linker = null;
+
+
+
+
+ /**
+ * Add a parameter.
+ *
+ * @param mixed $value Value of parameter
+ */
+ public function addParam($value)
+ {
+ $this->params[] = $value;
+ }
+
+
+ /**
+ * Add multiple parameters.
+ *
+ * @param mixed $value1 Value of first parameter
+ * @param mixed … Values of further parameters
+ */
+ public function addParams($value1)
+ {
+ $this->params = array_merge(
+ $this->params,
+ func_get_args()
+ );
+ }
+
+
+ /**
+ * Delete all stored parameters (from offset on).
+ *
+ * @param int $offset Offset-index
+ */
+ public function clearParams($offset=0)
+ {
+ $this->params = array_slice($this->params, 0, $offset);
+ }
+
+
+ /**
+ * Set linker instance for creating links.
+ *
+ * @param Linker $linker Linker instance for creating links
+ */
+ public function setLinker(\nre\core\Linker $linker)
+ {
+ $this->linker = $linker;
+ }
+
+
+ /**
+ * Get linker instance for creating links.
+ *
+ * @return \nre\core\Linker Linker instance for creating links
+ */
+ public function getLinker()
+ {
+ return $this->linker;
+ }
+
+ }
+
+?>
diff --git a/app/responses/MailResponse.inc b/app/responses/MailResponse.inc
new file mode 100644
index 00000000..b7d21a27
--- /dev/null
+++ b/app/responses/MailResponse.inc
@@ -0,0 +1,55 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\responses;
+
+
+ /**
+ * Representation of a mail-response.
+ *
+ * @author Oliver Hanraths
+ */
+ class MailResponse extends \nre\core\Response
+ {
+ /**
+ * Mail subject
+ *
+ * @var string
+ */
+ private $subject = null;
+
+
+
+
+ /**
+ * Set subject.
+ *
+ * @param string $subject Subject to set
+ */
+ public function setSubject($subject)
+ {
+ $this->subject = $subject;
+ }
+
+
+ /**
+ * Get subject.
+ *
+ * @return string Subject
+ */
+ public function getSubject()
+ {
+ return $this->subject;
+ }
+
+ }
+
+?>
diff --git a/app/views/QuesttypeView.inc b/app/views/QuesttypeView.inc
new file mode 100644
index 00000000..c42e2000
--- /dev/null
+++ b/app/views/QuesttypeView.inc
@@ -0,0 +1,76 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\views;
+
+
+ /**
+ * Abstract class for implementing a QuesttypeView.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeView extends \nre\core\View
+ {
+
+
+
+
+ /**
+ * Load and instantiate the QuesttypeView of a QuesttypeAgent.
+ *
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @param string $layoutName Name of Layout in use
+ * @param string $agentName Name of the Agent
+ * @param string $action Current Action
+ * @param bool $isToplevel Agent is a ToplevelAgent
+ */
+ public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false)
+ {
+ return new QuesttypeView($layoutName, $agentName, $action, $isToplevel);
+ }
+
+
+
+
+ /**
+ * Construct a new QuesttypeView.
+ *
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @param string $layoutName Name of Layout in use
+ * @param string $agentName Name of the Agent
+ * @param string $action Current Action
+ * @param bool $isToplevel Agent is a ToplevelAgent
+ */
+ protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false)
+ {
+ // Create template filename
+ // LayoutName
+ $fileName = ROOT.DS.\nre\configs\AppConfig::$dirs['questtypes'].DS.strtolower($agentName).DS.strtolower($layoutName).DS;
+
+ // Action
+ $fileName .= strtolower($action);
+
+ // File extension
+ $fileName .= \nre\configs\CoreConfig::getFileExt('views');
+
+
+ // Check template file
+ if(!file_exists($fileName)) {
+ throw new \nre\exceptions\ViewNotFoundException($fileName);
+ }
+
+ // Save filename
+ $this->templateFilename = $fileName;
+ }
+
+ }
+
+?>
diff --git a/bootstrap.inc b/bootstrap.inc
new file mode 100644
index 00000000..667204b6
--- /dev/null
+++ b/bootstrap.inc
@@ -0,0 +1,33 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ // Include required classes
+ require_once(ROOT.DS.'configs'.DS.'CoreConfig.inc');
+ require_once(ROOT.DS.\nre\configs\CoreConfig::getClassDir('core').DS.'Autoloader.inc');
+
+
+ // Set PHP-logging
+ ini_set('error_log', ROOT.DS.\nre\configs\CoreConfig::getClassDir('logs').DS.'php'.\nre\configs\CoreConfig::getFileExt('logs'));
+
+ // Register autoloader
+ \nre\core\Autoloader::register();
+
+
+ // Initialize WebApi
+ $webApi = new \nre\apis\WebApi();
+
+ // Run WebApi
+ $webApi->run();
+
+ // Render output
+ $webApi->render();
+
+?>
diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc
new file mode 100644
index 00000000..910f2d7c
--- /dev/null
+++ b/configs/AppConfig.inc
@@ -0,0 +1,406 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace nre\configs;
+
+
+ /**
+ * Application configuration.
+ *
+ * This class contains static variables with configuration values for
+ * the specific application.
+ *
+ * @author Oliver Hanraths
+ */
+ final class AppConfig
+ {
+
+ /**
+ * Application values
+ *
+ * @static
+ * @var array
+ */
+ public static $app = array(
+ 'name' => 'Questlab',
+ 'genericname' => 'The Legend of Z',
+ 'namespace' => 'hhu\\z\\',
+ 'timeZone' => 'Europe/Berlin',
+ 'mailsender' => '',
+ 'mailcontact' => '',
+ 'registration_host' => '',
+ 'languages' => array(
+ 'de' => 'de_DE.utf8'
+ )
+ );
+
+
+ /**
+ * Default values
+ *
+ * @static
+ * @var array
+ */
+ public static $defaults = array(
+ 'toplevel' => 'html',
+ 'toplevel-error' => 'fault',
+ 'toplevel-mail' => 'textmail',
+ 'toplevel-htmlmail' => 'htmlmail',
+ 'intermediate' => 'introduction',
+ 'intermediate-error' => 'error',
+ 'intermediate-mail' => 'mail'
+ );
+
+
+ /**
+ * Directories
+ *
+ * @static
+ * @var array
+ */
+ public static $dirs = array(
+ 'locale' => 'locale',
+ 'media' => 'media',
+ 'seminarymedia' => 'seminarymedia',
+ 'questtypes' => 'questtypes',
+ 'temporary' => 'tmp',
+ 'uploads' => 'uploads',
+ 'seminaryuploads' => 'seminaryuploads'
+ );
+
+
+ /**
+ * Media sizes
+ *
+ * @static
+ * @var array
+ */
+ public static $media = array(
+ 'questgroup' => array(
+ 'width' => 480,
+ 'height' => 5000
+ ),
+ 'quest' => array(
+ 'width' => 200,
+ 'height' => 200
+ ),
+ 'avatar' => array(
+ 'width' => 500,
+ 'height' => 500
+ ),
+ 'charactergroup' => array(
+ 'width' => 80,
+ 'height' => 80
+ ),
+ 'charactergroupsquest' => array(
+ 'width' => 80,
+ 'height' => 80
+ )
+ );
+
+
+ /**
+ * Allowed mimetypes with sizes
+ *
+ * @static
+ * @var array
+ */
+ public static $mimetypes = array(
+ 'icons' => array(
+ array(
+ 'mimetype' => 'image/jpeg',
+ 'size' => 102400
+ ),
+ array(
+ 'mimetype' => 'image/png',
+ 'size' => 204800
+ )
+ ),
+ 'moodpics' => array(
+ array(
+ 'mimetype' => 'image/jpeg',
+ 'size' => 524288
+ ),
+ array(
+ 'mimetype' => 'image/png',
+ 'size' => 1048576
+ )
+ ),
+ 'charactergroupsquests' => array(
+ array(
+ 'mimetype' => 'image/jpeg',
+ 'size' => 1048576
+ )
+ ),
+ 'questtypes' => array(
+ array(
+ 'mimetype' => 'image/jpeg',
+ 'size' => 524288
+ ),
+ array(
+ 'mimetype' => 'image/png',
+ 'size' => 1048576
+ )
+ ),
+ 'map' => array(
+ array(
+ 'mimetype' => 'image/jpeg',
+ 'size' => 5242880
+ ),
+ array(
+ 'mimetype' => 'image/png',
+ 'size' => 10485760
+ )
+ )
+ );
+
+
+ /**
+ * Miscellaneous settings
+ *
+ * @static
+ * @var array
+ */
+ public static $misc = array(
+ 'ranking_range' => 2,
+ 'achievements_range' => 3,
+ 'title_delimiter' => ' – ',
+ 'lists_limit' => 10,
+ 'imagesource_length' => 30
+ );
+
+
+ /**
+ * Validation settings for user input
+ *
+ * @static
+ * @var array
+ */
+ public static $validation = array(
+ 'username' => array(
+ 'minlength' => 5,
+ 'maxlength' => 12,
+ 'regex' => '/^\w*$/'
+ ),
+ 'email' => array(
+ 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU'
+ ),
+ 'prename' => array(
+ 'minlength' => 2,
+ 'maxlength' => 32,
+ 'regex' => '/^(\S| )*$/'
+ ),
+ 'surname' => array(
+ 'minlength' => 2,
+ 'maxlength' => 32,
+ 'regex' => '/^\S*$/'
+ ),
+ 'password' => array(
+ 'minlength' => 5,
+ 'maxlength' => 64
+ ),
+ 'charactername' => array(
+ 'minlength' => 5,
+ 'maxlength' => 12,
+ 'regex' => '/^\w*$/'
+ ),
+ 'charactergroupsgroupname' => array(
+ 'minlength' => 4,
+ 'maxlength' => 32,
+ 'regex' => '/^(\S| )*$/'
+ ),
+ 'preferred' => array(
+ 'regex' => '/^(0|1)$/'
+ ),
+ 'charactergroupname' => array(
+ 'minlength' => 4,
+ 'maxlength' => 32,
+ 'regex' => '/^(\S| )*$/'
+ ),
+ 'motto' => array(
+ 'maxlength' => 128
+ ),
+ 'title' => array(
+ 'minlength' => 4,
+ 'maxlength' => 32,
+ 'regex' => '/^(\S| )*$/'
+ ),
+ 'xps' => array(
+ 'minlength' => 1,
+ 'regex' => '/^(\d*)$/'
+ ),
+ 'course' => array(
+ 'maxlength' => 128
+ ),
+ 'charactertypename' => array(
+ 'minlength' => 1,
+ 'maxlength' => 32
+ ),
+ 'questgroupshierarchytitle' => array(
+ 'minlength' => 1,
+ 'maxlength' => 64
+ ),
+ 'deadline' => array(
+ 'regex' => '/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})?$/'
+ ),
+ 'choice' => array(
+ 'minlength' => 1,
+ 'maxlength' => 128
+ ),
+ 'answer' => array(
+ 'minlength' => 1,
+ 'maxlength' => 255
+ )
+ );
+
+
+ /**
+ * Routes
+ *
+ * @static
+ * @var array
+ */
+ public static $routes = array(
+ array('^users/all/?$', 'users/index/all', true),
+ array('^users/([^/]+)/(edit|delete)/?$', 'users/$2/$1', true),
+ array('^users/(?!(index|login|register|logout|manage|create|edit|delete))/?', 'users/user/$1', true),
+ array('^seminaries/([^/]+)/(edit|copy|delete)/?$', 'seminaries/$2/$1', true),
+ array('^seminaries/(?!(index|create|edit|copy|delete|calculatexps))/?', 'seminaries/seminary/$1', true),
+ array('^xplevels/([^/]+)/(manage)/?$', 'xplevels/$2/$1', true),
+ array('^questgroupshierarchy/([^/]+)/create/?$', 'questgroupshierarchy/create/$1', true),
+ array('^questgroupshierarchy/([^/]+)/([^/]+)/(edit|delete|moveup|movedown)/?$', 'questgroupshierarchy/$3/$1/$2', true),
+ array('^questgroups/([^/]+)/create/?$', 'questgroups/create/$1', true),
+ array('^questgroups/([^/]+)/([^/]+)/(edit|edittexts|delete|moveup|movedown)/?$','questgroups/$3/$1/$2', true),
+ array('^questgroups/([^/]+)/([^/]+)/?$', 'questgroups/questgroup/$1/$2', true),
+ array('^quests/([^/]+)/?$', 'quests/index/$1', true),
+ array('^quests/([^/]+)/all/?$', 'quests/index/$1/all', true),
+ array('^quests/([^/]+)/([^/]+)/(create|createmedia)/?$', 'quests/$3/$1/$2', true),
+ array('^quests/([^/]+)/([^/]+)/([^/]+)/(submissions|edit|edittask|edittexts|delete)/?$','quests/$4/$1/$2/$3', true),
+ array('^quests/([^/]+)/([^/]+)/([^/]+)/(submission)/([^/]+)/?$', 'quests/$4/$1/$2/$3/$5', true),
+ array('^quests/(?!(index|create|createmedia))/?', 'quests/quest/$1', true),
+ array('^characters/([^/]+)/(register|manage)/?$', 'characters/$2/$1', true),
+ array('^characters/([^/]+)/?$', 'characters/index/$1', true),
+ array('^characters/([^/]+)/all/?$', 'characters/index/$1/all', true),
+ array('^characters/([^/]+)/([^/]+)/(edit|delete)/?$', 'characters/$3/$1/$2', true),
+ array('^characters/([^/]+)/(?!(index|register|manage))/?', 'characters/character/$1/$2', true),
+ array('^charactertypes/([^/]+)/?$', 'charactertypes/index/$1', true),
+ array('^charactertypes/([^/]+)/create/?$', 'charactertypes/create/$1', true),
+ array('^charactertypes/([^/]+)/([^/]+)/(edit|delete)/?$', 'charactertypes/$3/$1/$2', true),
+ array('^charactergroups/([^/]+)/?$', 'charactergroups/index/$1', true),
+ array('^charactergroups/([^/]+)/(create)/?$', 'charactergroups/creategroupsgroup/$1/$2', true),
+ array('^charactergroups/([^/]+)/([^/]+)/?$', 'charactergroups/groupsgroup/$1/$2', true),
+ array('^charactergroups/([^/]+)/([^/]+)/(edit|delete)/?$', 'charactergroups/$3groupsgroup/$1/$2', true),
+ array('^charactergroups/([^/]+)/([^/]+)/(create)/?$', 'charactergroups/creategroup/$1/$2/$3', true),
+ array('^charactergroups/([^/]+)/([^/]+)/([^/]+)/?$', 'charactergroups/group/$1/$2/$3', true),
+ array('^charactergroups/([^/]+)/([^/]+)/([^/]+)/(manage|edit|delete)/?$', 'charactergroups/$4group/$1/$2/$3', true),
+ array('^charactergroupsquests/([^/]+)/([^/]+)/create/?$', 'charactergroupsquests/create/$1/$2', true),
+ array('^charactergroupsquests/([^/]+)/([^/]+)/([^/]+)/?$', 'charactergroupsquests/quest/$1/$2/$3', true),
+ array('^charactergroupsquests/([^/]+)/([^/]+)/([^/]+)/(edit|delete|manage)/?$', 'charactergroupsquests/$4/$1/$2/$3', true),
+ array('^achievements/([^/]+)/(create|manage)/?$', 'achievements/$2/$1', true),
+ array('^achievements/([^/]+)/([^/]+)/(edit|conditions|moveup|movedown|delete)/?$', 'achievements/$3/$1/$2', true),
+ array('^achievements/([^/]+)/?$', 'achievements/index/$1', true),
+ array('^library/([^/]+)/?$', 'library/index/$1', true),
+ array('^library/([^/]+)/create/?$', 'library/create/$1', true),
+ array('^library/([^/]+)/([^/]+)/?$', 'library/topic/$1/$2', true),
+ array('^library/([^/]+)/([^/]+)/(edit|delete|manage)/?$', 'library/$3/$1/$2', true),
+ array('^map/([^/]+)/?$', 'map/index/$1', true),
+ array('^map/([^/]+)/(edit)/?$', 'map/$2/$1', true),
+ array('^pages/([^/]+)/(edit|delete)$', 'pages/$2/$1', true),
+ array('^pages/(?!(create|edit|delete))/?', 'pages/page/$1', true),
+ array('^media/(.*)$', 'media/$1?layout=binary', false),
+ array('^uploads/(.*)$', 'uploads/$1?layout=binary', false)
+ );
+
+
+ /**
+ * Reverse routes
+ *
+ * @static
+ * @var array
+ */
+ public static $reverseRoutes = array(
+ array('^users/index/all$', 'users/all', true),
+ array('^users/user/(.*)$', 'users/$1', true),
+ array('^users/([^/]+)/(.*)$', 'users/$2/$1', true),
+ array('^seminaries/seminary/(.*)$', 'seminaries/$1', false),
+ array('^xplevels/(manage)/(.*)$', 'xplevels/$2/$1', false),
+ array('^questgroupshierarchy/create/(.*)$', 'questgroupshierarchy/$1/create', true),
+ array('^questgroupshierarchy/([^/]+)/(.*)$', 'questgroupshierarchy/$2/$1', true),
+ array('^questgroups/create/(.*)$', 'questgroups/$1/create', true),
+ array('^questgroups/questgroup/(.*)$', 'questgroups/$1', true),
+ array('^questgroups/(edit|edittexts|delete|moveup|movedown)/(.*)$', 'questgroups/$2/$1', true),
+ array('^quests/index/(.+)$', 'quests/$1', true),
+ array('^quests/quest/(.*)$', 'quests/$1', true),
+ array('^quests/(create|createmedia)/(.*)$', 'quests/$2/$1', true),
+ array('^quests/(submissions|edit|edittask|edittexts|delete)/([^/]+)/([^/]+)/([^/]+)$', 'quests/$2/$3/$4/$1', true),
+ array('^quests/(submission)/([^/]+)/([^/]+)/([^/]+)/([^/]+)$', 'quests/$2/$3/$4/$1/$5', true),
+ array('^characters/(index|character)/(.*)$', 'characters/$2', true),
+ array('^characters/(register|manage)/([^/]+)$', 'characters/$2/$1', true),
+ array('^characters/(edit|delete)/([^/]+)/([^/]+)$', 'characters/$2/$3/$1', true),
+ array('^charactertypes/index/([^/]+)$', 'charactertypes/$1', true),
+ array('^charactertypes/create/([^/]+)$', 'charactertypes/$1/create', true),
+ array('^charactertypes/(edit|delete)/([^/]+)/([^/]+)$', 'charactertypes/$2/$3/$1', true),
+ array('^charactergroups/index/([^/]+)$', 'charactergroups/$1', true),
+ array('^charactergroups/creategroupsgroup/([^/]+)$', 'charactergroups/$1/create', true),
+ array('^charactergroups/groupsgroup/([^/]+)/([^/]+)$', 'charactergroups/$1/$2', true),
+ array('^charactergroups/(edit|delete)groupsgroup/([^/]+)/([^/]+)$', 'charactergroups/$2/$3/$1', true),
+ array('^charactergroups/creategroup/([^/]+)/([^/]+)$', 'charactergroups/$1/$2/create', true),
+ array('^charactergroups/group/([^/]+)/([^/]+)/([^/]+)$', 'charactergroups/$1/$2/$3', true),
+ array('^charactergroups/(manage|edit|delete)group/([^/]+)/([^/]+)/([^/]+)$', 'charactergroups/$2/$3/$4/$1', true),
+ array('^charactergroupsquests/create/([^/]+)/([^/]+)/?$', 'charactergroupsquests/$1/$2/create', true),
+ array('^charactergroupsquests/quest/(.*)$', 'charactergroupsquests/$1', true),
+ array('^charactergroupsquests/(edit|delete|manage)/([^/]+)/([^/]+)/([^/]+)$', 'charactergroupsquests/$2/$3/$4/$1', true),
+ array('^achievements/index/(.*)$', 'achievements/$1', true),
+ array('^achievements/(create|manage)/(.*)$', 'achievements/$2/$1', true),
+ array('^achievements/(edit|conditions|moveup|movedown|delete)/(.*)$', 'achievements/$2/$1', true),
+ array('^library/index/([^/]+)/?$', 'library/$1', true),
+ array('^library/create/([^/]+)/?$', 'library/$1/create', true),
+ array('^library/topic/([^/]+)/([^/]+)/?$', 'library/$1/$2', true),
+ array('^library/(edit|delete|manage)/([^/]+)/([^/]+)/?$', 'library/$2/$3/$1', true),
+ array('^map/index/(.*)$', 'map/$1', true),
+ array('^map/(edit)/(.*)$', 'map/$2/$1', true),
+ array('^pages/page/(.*+)$', 'pages/$1', true),
+ array('^pages/(edit|delete)/([^/]+)$', 'pages/$2/$1', true)
+ );
+
+
+ /**
+ * Database connection settings
+ *
+ * @static
+ * @var array
+ */
+ public static $database = array(
+ 'user' => 'z',
+ 'host' => 'localhost',
+ 'password' => 'legendofZ',
+ 'db' => 'z'
+ );
+
+
+ /**
+ * Mailserver connection settings
+ *
+ * @static
+ * @var array
+ */
+ public static $mail = array(
+ 'host' => '',
+ 'port' => 465,
+ 'username' => '',
+ 'password' => '',
+ 'secure' => '',
+ 'charset' => 'UTF-8'
+ );
+
+ }
+
+?>
diff --git a/configs/CoreConfig.inc b/configs/CoreConfig.inc
new file mode 100644
index 00000000..c2c2605d
--- /dev/null
+++ b/configs/CoreConfig.inc
@@ -0,0 +1,167 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\configs;
+
+
+ /**
+ * Core configuration.
+ *
+ * This class contains static variables with configuration values.
+ *
+ * @author coderkun
+ */
+ final class CoreConfig
+ {
+
+ /**
+ * Core values
+ *
+ * @static
+ * @var array
+ */
+ public static $core = array(
+ 'namespace' => 'nre\\',
+ );
+
+
+ /**
+ * Directories
+ *
+ * @static
+ * @var array
+ */
+ public static $dirs = array(
+ 'core' => 'core',
+ 'publicDir' => 'www'
+ );
+
+
+ /**
+ * File extensions
+ *
+ * @static
+ * @var array
+ */
+ public static $fileExts = array(
+ 'default' => 'inc',
+ 'views' => 'tpl',
+ 'logs' => 'log',
+ );
+
+
+ /**
+ * Default values
+ *
+ * @static
+ * @var array
+ */
+ public static $defaults = array(
+ 'action' => 'index',
+ 'errorFile' => 'error',
+ 'inlineErrorFile' => 'inlineerror'
+ );
+
+
+ /**
+ * Miscellaneous settings
+ *
+ * @static
+ * @var array
+ */
+ public static $misc = array(
+ 'fileExtDot' => '.'
+ );
+
+
+ /**
+ * Logging settings
+ *
+ * @static
+ * @var array
+ */
+ public static $log = array(
+ 'filename' => 'errors',
+ 'format' => 'Fehler %d: %s in %s, Zeile %d'
+ );
+
+
+ /**
+ * Class-specific settings
+ *
+ * @static
+ * @var array
+ */
+ public static $classes = array(
+ 'linker' => array(
+ 'url' => array(
+ 'length' => 128,
+ 'delimiter' => '-'
+ )
+ )
+ );
+
+
+
+
+ /**
+ * Determine the directory for a specific classtype.
+ *
+ * @param string $classType Classtype to get directory of
+ * @return string Directory of given classtype
+ */
+ public static function getClassDir($classType)
+ {
+ // Default directory (for core classes)
+ $classDir = self::$dirs['core'];
+
+ // Configurable directory
+ if(array_key_exists($classType, self::$dirs)) {
+ $classDir = self::$dirs[$classType];
+ }
+ else
+ {
+ // Default directory for classtype
+ if(is_dir(ROOT.DS.$classType)) {
+ $classDir = $classType;
+ }
+ }
+
+
+ // Return directory
+ return $classDir;
+ }
+
+
+ /**
+ * Determine the file extension for a specific filetype.
+ *
+ * @param string $fileType Filetype to get file extension of
+ * @return string File extension of given filetype
+ */
+ public static function getFileExt($fileType)
+ {
+ // Default file extension
+ $fileExt = self::$fileExts['default'];
+
+ // Configurable file extension
+ if(array_key_exists($fileType, self::$fileExts)) {
+ $fileExt = self::$fileExts[$fileType];
+ }
+
+
+ // Return file extension
+ return self::$misc['fileExtDot'].$fileExt;
+ }
+
+ }
+
+?>
diff --git a/controllers/AchievementsController.inc b/controllers/AchievementsController.inc
new file mode 100644
index 00000000..d57a47a4
--- /dev/null
+++ b/controllers/AchievementsController.inc
@@ -0,0 +1,959 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to list Achievements.
+ *
+ * @author Oliver Hanraths
+ */
+ class AchievementsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('achievements', 'seminaries', 'media', 'characters', 'quests');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator', 'user'),
+ 'moveup' => array('admin', 'moderator', 'user'),
+ 'movedown' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'conditions' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin'),
+ 'moveup' => array('admin', 'moderator'),
+ 'movedown' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'conditions' => array('admin', 'moderator'),
+ 'delete' => array('admin')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List Achievements of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ */
+ public function index($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character
+ $character = SeminaryController::$character;
+
+ // Get seldom Achievements
+ $seldomAchievements = $this->Achievements->getSeldomAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range'], false);
+ foreach($seldomAchievements as &$achievement) {
+ $achievement['achieved'] = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']);
+ }
+
+ // Get Characters with the most Achievements
+ $successfulCharacters = $this->Characters->getCharactersWithMostAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range'], false);
+
+ // Get total count of Achievements for Seminary
+ $achievementsCount = $this->Achievements->getAchievementsCountForSeminary($seminary['id'], false);
+
+ // Get achieved Achievements
+ $achievedAchievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']);
+
+ // Get unachieved Achievements
+ $unachievedAchievements = array_merge(
+ $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id'], false, false),
+ $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary($seminary['id'])
+ );
+ usort($unachievedAchievements, function($a, $b) {
+ if($a['pos'] == $b['pos']) {
+ return 0;
+ }
+ return ($a['pos'] > $b['pos']) ? 1 : -1;
+ });
+ foreach($unachievedAchievements as &$achievement)
+ {
+ // Get Character progress
+ if($achievement['progress'])
+ {
+ $conditions = array();
+ switch($achievement['condition'])
+ {
+ // Character conditions
+ case 'character':
+ $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']);
+ foreach($conditionsCharacter as &$condition)
+ {
+ $conditions[] = array(
+ 'func' => 'getAchievementConditionCharacterProgress',
+ 'params' => array(
+ $condition['field'],
+ $condition['value'],
+ $character['id']
+ )
+ );
+ }
+ break;
+ // Quest conditions
+ case 'quest':
+ $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']);
+ foreach($conditionsQuest as &$condition)
+ {
+ $conditions[] = array(
+ 'func' => 'getAchievementConditionQuestProgress',
+ 'params' => array(
+ $condition['field'],
+ $condition['count'],
+ $condition['value'],
+ $condition['status'],
+ $condition['groupby'],
+ $condition['quest_id'],
+ $character['id']
+ )
+ );
+ }
+ break;
+ // Achievement conditions
+ case 'achievement':
+ $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']);
+ foreach($conditionsAchievement as &$condition)
+ {
+ $conditions[] = array(
+ 'func' => 'getAchievementConditionAchievementProgress',
+ 'params' => array(
+ $condition['field'],
+ $condition['count'],
+ $condition['value'],
+ $condition['groupby'],
+ $condition['meta_achievement_id'],
+ $character['id']
+ )
+ );
+ }
+ break;
+ }
+
+ $characterProgresses = array();
+ foreach($conditions as &$condition)
+ {
+ // Calculate progress of condition
+ $characterProgresses[] = call_user_func_array(
+ array(
+ $this->Achievements,
+ $condition['func']
+ ),
+ $condition['params']
+ );
+ }
+
+ $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses);
+ }
+ }
+
+ // Get ranking
+ $character['rank'] = $this->Achievements->getCountRank($seminary['id'], count($achievedAchievements));
+
+
+ // Set title
+ $this->addTitleLocalized('Achievements');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('character', $character);
+ $this->set('seldomAchievements', $seldomAchievements);
+ $this->set('successfulCharacters', $successfulCharacters);
+ $this->set('achievementsCount', $achievementsCount);
+ $this->set('achievedAchievements', $achievedAchievements);
+ $this->set('unachievedAchievements', $unachievedAchievements);
+ }
+
+
+ /**
+ * Action: manage.
+ *
+ * Manage Achievements of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ */
+ public function manage($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievements
+ $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']);
+
+
+ // Set title
+ $this->addTitleLocalized('Manage Achievements');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('achievements', $achievements);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ */
+ public function create($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievements conditions
+ $conditions = $this->Achievements->getAchievementsConditions();
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $title = '';
+ $description = '';
+ $progress = 0;
+ $hidden = 0;
+ $onlyOnce = 0;
+ $allConditions = 1;
+ $deadline = '';
+ $condition = $conditions[0]['condition'];
+ $fields = array('title', 'deadline');
+ $validation = array();
+
+ // Create Achievement
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Achievements->achievementTitleExists($seminary['id'], $title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $description = $this->request->getPostParam('description');
+ $progress = !is_null($this->request->getPostParam('progress'));
+ $hidden = !is_null($this->request->getPostParam('hidden'));
+ $onlyOnce = !is_null($this->request->getPostParam('only_once'));
+ $allConditions = !is_null($this->request->getPostParam('all_conditions'));
+ $deadline = $this->request->getPostParam('deadline');
+ $condition = $this->request->getPostParam('condition');
+
+ // Validate condition
+ $conditionIndex = null;
+ foreach($conditions as $index => &$c) {
+ if($condition == $c['condition']) {
+ $conditionIndex = $index;
+ }
+ }
+ if(is_null($conditionIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($condition);
+ }
+
+ // Validate images
+ $images = array(
+ 'unachieved_image' => null,
+ 'achieved_image' => null
+ );
+ foreach($images as $key => $image)
+ {
+ if(!empty($_FILES) && array_key_exists($key, $_FILES) && $_FILES[$key]['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $images[$key] = $_FILES[$key];
+
+ // Check error
+ if($images[$key]['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'image', 'error', $image[$key]['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $images[$key]['mimetype'] = \hhu\z\Utils::getMimetype($images[$key]['tmp_name'], $images[$key]['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $images[$key]['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'image', 'mimetype', $images[$key]['mimetype']);
+ }
+ elseif($images[$key]['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'image', 'size', $mediaMimetype['size']);
+ }
+ }
+ }
+
+ // Create Achievement
+ if($validation === true)
+ {
+ $achievementId = $this->Achievements->createAchievement(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $conditions[$conditionIndex]['id'],
+ $title,
+ $description,
+ $progress,
+ $hidden,
+ $onlyOnce,
+ $allConditions,
+ (!empty($deadline)) ? $deadline : NULL
+ );
+ $achievement = $this->Achievements->getAchievementById($achievementId);
+
+ // Upload images
+ foreach($images as $key => &$image)
+ {
+ if(!is_null($image))
+ {
+ $image['media_id'] = $this->Media->createAchievementMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('achievement-%d', $achievement['id']).(substr($key, 0, 2) == 'un' ? '-locked' : NULL),
+ '',
+ $image['mimetype'],
+ $image['tmp_name']
+ );
+ }
+ }
+ if(!is_null($images['unachieved_image']) && $images['unachieved_image']['media_id'] !== false) {
+ $this->Achievements->setUnachievedMediaForAchievement($achievement['id'], $images['unachieved_image']['media_id']);
+ }
+ if(!is_null($images['achieved_image']) && $images['achieved_image']['media_id'] !== false) {
+ $this->Achievements->setAchievedMediaForAchievement($achievement['id'], $images['achieved_image']['media_id']);
+ }
+
+ // Redirect to condition editing
+ $this->redirect($this->linker->link(array('conditions', $seminary['url'], $achievement['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Create Achievement');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('title', $title);
+ $this->set('description', $description);
+ $this->set('progress', $progress);
+ $this->set('hidden', $hidden);
+ $this->set('onlyOnce', $onlyOnce);
+ $this->set('allConditions', $allConditions);
+ $this->set('deadline', $deadline);
+ $this->set('condition', $condition);
+ $this->set('conditions', $conditions);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: moveup.
+ *
+ * Move an Achievement up (decrement position).
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $achievementUrl URL-title of Achievement
+ */
+ public function moveup($seminaryUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl);
+
+ // Set position
+ $this->Achievements->moveAchievement($achievement, true);
+
+
+ // Redirect
+ $this->redirect($this->linker->link(array('manage', $seminary['url']), 1, true, null, false, $achievement['url']));
+ }
+
+
+ /**
+ * Action: movedown.
+ *
+ * Move an Achievement down (increment position).
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $achievementUrl URL-title of Achievement
+ */
+ public function movedown($seminaryUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl);
+
+ // Set position
+ $this->Achievements->moveAchievement($achievement, false);
+
+
+ // Redirect
+ $this->redirect($this->linker->link(array('manage', $seminary['url']), 1, true, null, false, $achievement['url']));
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit an Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $achievementUrl URL-title of Achievement to edit
+ */
+ public function edit($seminaryUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl);
+
+ // Get Achievements conditions
+ $conditions = $this->Achievements->getAchievementsConditions();
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $title = $achievement['title'];
+ $description = $achievement['description'];
+ $progress = $achievement['progress'];
+ $hidden = $achievement['hidden'];
+ $onlyOnce = $achievement['only_once'];
+ $allConditions = $achievement['all_conditions'];
+ $deadline = $achievement['deadline'];
+ $condition = $achievement['condition'];
+ $fields = array('title', 'deadline');
+ $validation = array();
+
+ // Edit Achievement
+ if($this->request->getRequestMethod() == 'POST' && (!is_null($this->request->getPostParam('edit')) || !is_null($this->request->getPostParam('edit-conditions'))))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Achievements->achievementTitleExists($seminary['id'], $title, $achievement['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $description = $this->request->getPostParam('description');
+ $progress = !is_null($this->request->getPostParam('progress'));
+ $hidden = !is_null($this->request->getPostParam('hidden'));
+ $onlyOnce = !is_null($this->request->getPostParam('only_once'));
+ $allConditions = !is_null($this->request->getPostParam('all_conditions'));
+ $deadline = $this->request->getPostParam('deadline');
+ $condition = $this->request->getPostParam('condition');
+
+ // Validate condition
+ $conditionIndex = null;
+ foreach($conditions as $index => &$c) {
+ if($condition == $c['condition']) {
+ $conditionIndex = $index;
+ }
+ }
+ if(is_null($conditionIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($condition);
+ }
+
+ // Validate images
+ $images = array(
+ 'unachieved_image' => null,
+ 'achieved_image' => null
+ );
+ foreach($images as $key => $image)
+ {
+ if(!empty($_FILES) && array_key_exists($key, $_FILES) && $_FILES[$key]['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $images[$key] = $_FILES[$key];
+
+ // Check error
+ if($images[$key]['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'image', 'error', $image[$key]['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $images[$key]['mimetype'] = \hhu\z\Utils::getMimetype($images[$key]['tmp_name'], $images[$key]['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $images[$key]['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'image', 'mimetype', $images[$key]['mimetype']);
+ }
+ elseif($images[$key]['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'image', 'size', $mediaMimetype['size']);
+ }
+ }
+ }
+
+ // Edit Achievement
+ if($validation === true)
+ {
+ $this->Achievements->editAchievement(
+ $achievement['id'],
+ $conditions[$conditionIndex]['id'],
+ $title,
+ $description,
+ $progress,
+ $hidden,
+ $onlyOnce,
+ $allConditions,
+ (!empty($deadline)) ? $deadline : NULL
+ );
+
+ // Remove old conditions
+ if($conditions[$conditionIndex]['condition'] != $achievement['condition'])
+ {
+ switch($achievement['condition'])
+ {
+ // Date conditions
+ case 'date':
+ foreach($this->Achievements->getAchievementConditionsDate($achievement['id']) as $c) {
+ $this->Achievements->deleteAchievementConditionDate($c['id']);
+ }
+ break;
+ // Character conditions
+ case 'character':
+ foreach($this->Achievements->getAchievementConditionsCharacter($achievement['id']) as $c) {
+ $this->Achievements->deleteAchievementConditionCharacter($c['id']);
+ }
+ break;
+ // Quest conditions
+ case 'quest':
+ foreach($this->Achievements->getAchievementConditionsQuest($achievement['id']) as $c) {
+ $this->Achievements->deleteAchievementConditionQuest($c['id']);
+ }
+ break;
+ // Achievement conditions
+ case 'achievement':
+ foreach($this->Achievements->getAchievementConditionsAchievement($achievement['id']) as $c) {
+ $this->Achievements->deleteAchievementConditionAchievement($c['id']);
+ }
+ break;
+ }
+ }
+
+ // Upload images
+ foreach($images as $key => &$image)
+ {
+ if(!is_null($image))
+ {
+ $image['media_id'] = $this->Media->createAchievementMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('achievement-%d', $achievement['id']).(substr($key, 0, 2) == 'un' ? '-locked' : NULL),
+ '',
+ $image['mimetype'],
+ $image['tmp_name']
+ );
+ }
+ }
+ if(!is_null($images['unachieved_image']) && $images['unachieved_image']['media_id'] !== false) {
+ $this->Achievements->setUnachievedMediaForAchievement($achievement['id'], $images['unachieved_image']['media_id']);
+ }
+ if(!is_null($images['achieved_image']) && $images['achieved_image']['media_id'] !== false) {
+ $this->Achievements->setAchievedMediaForAchievement($achievement['id'], $images['achieved_image']['media_id']);
+ }
+
+ // Redirect
+ if(!is_null($this->request->getPostParam('edit-conditions'))) {
+ // To condition editing
+ $this->redirect($this->linker->link(array('conditions', $seminary['url'], $achievement['url']), 1));
+ }
+ else {
+ // To overview
+ $this->redirect($this->linker->link(array('manage', $seminary['url']), 1, true, null, false, $achievement['url']));
+ }
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Edit Achievement');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('achievement', $achievement);
+ $this->set('title', $title);
+ $this->set('description', $description);
+ $this->set('progress', $progress);
+ $this->set('hidden', $hidden);
+ $this->set('onlyOnce', $onlyOnce);
+ $this->set('allConditions', $allConditions);
+ $this->set('deadline', $deadline);
+ $this->set('condition', $condition);
+ $this->set('conditions', $conditions);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: conditions.
+ *
+ * Edit conditions of an Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $achievementUrl URL-title of Achievement to edit
+ */
+ public function conditions($seminaryUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl);
+
+ // Get conditions
+ $conditions = array();
+ switch($achievement['condition'])
+ {
+ // Date conditions
+ case 'date':
+ $conditions = $this->Achievements->getAchievementConditionsDate($achievement['id']);
+ break;
+ // Character conditions
+ case 'character':
+ $conditions = $this->Achievements->getAchievementConditionsCharacter($achievement['id']);
+ break;
+ // Quest conditions
+ case 'quest':
+ $conditions = $this->Achievements->getAchievementConditionsQuest($achievement['id']);
+ break;
+ // Achievement conditions
+ case 'achievement':
+ $conditions = $this->Achievements->getAchievementConditionsAchievement($achievement['id']);
+ break;
+ }
+
+ // Values
+ $characterFields = array(
+ 'id', 'created', 'user_id', 'name', 'url', 'xps', 'quest_xps', 'avatar_id',
+ 'charactertype_id', 'charactertype_name', 'charactertype_url',
+ 'xplevel_id', 'xplevel'
+ );
+ $questFields = array(
+ 'id', 'quest_id', 'character_id', 'created', 'status'
+ );
+ $achievementFields = array(
+ 'achievement_id', 'character_id', 'created'
+ );
+ $quests = $this->Quests->getQuestsForSeminary($seminary['id']);
+ $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']);
+ $deletes = array();
+
+ // Save conditions
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get entered conditions
+ $selectedConditions = $this->request->getPostParam('conditions');
+ $deletes = $this->request->getPostParam('deletes');
+ if(!is_array($deletes)) {
+ $deletes = array();
+ }
+
+ // Edit or delete conditions
+ foreach($conditions as &$condition)
+ {
+ $selectedCondition = $selectedConditions[$condition['id']];
+ switch($achievement['condition'])
+ {
+ // Date conditions
+ case 'date':
+ if(array_key_exists($condition['id'], $deletes)) {
+ $this->Achievements->deleteAchievementConditionDate($condition['id']);
+ }
+ else {
+ $this->Achievements->editAchievementConditionDate(
+ $condition['id'],
+ $selectedCondition['select']
+ );
+ }
+ break;
+ // Character conditions
+ case 'character':
+ if(array_key_exists($condition['id'], $deletes)) {
+ $this->Achievements->deleteAchievementConditionCharacter($condition['id']);
+ }
+ else {
+ $this->Achievements->editAchievementConditionCharacter(
+ $condition['id'],
+ $selectedCondition['field'],
+ $selectedCondition['value']
+ );
+ }
+ break;
+ // Quest conditions
+ case 'quest':
+ if(array_key_exists($condition['id'], $deletes)) {
+ $this->Achievements->deleteAchievementConditionQuest($condition['id']);
+ }
+ else
+ {
+ // Get selected Quest
+ $questIndex = null;
+ if(!empty($selectedCondition['quest'])) {
+ foreach($quests as $index => &$quest) {
+ if($quest['url'] == $selectedCondition['quest']) {
+ $questIndex = $index;
+ }
+ }
+ }
+
+ // Edit condition
+ $this->Achievements->editAchievementConditionQuest(
+ $condition['id'],
+ $selectedCondition['field'],
+ array_key_exists('count', $selectedCondition),
+ $selectedCondition['value'],
+ (!is_null($questIndex)) ? $quests[$questIndex]['id'] : null,
+ ($selectedCondition['status'] != '') ? $selectedCondition['status'] : null,
+ (!empty($selectedCondition['groupby'])) ? $selectedCondition['groupby'] : null
+ );
+ }
+ break;
+ // Achievement conditions
+ case 'achievement':
+ if(array_key_exists($condition['id'], $deletes)) {
+ $this->Achievements->deleteAchievementConditionAchievement($condition['id']);
+ }
+ else
+ {
+ // Get selected Achievement
+ $achievmentIndex = null;
+ if(!empty($selectedCondition['achievement'])) {
+ foreach($achievements as $index => &$a) {
+ if($a['url'] == $selectedCondition['achievement']) {
+ $achievementIndex = $index;
+ }
+ }
+ }
+
+ // Edit condition
+ $this->Achievements->editAchievementConditionAchievement(
+ $condition['id'],
+ $selectedCondition['field'],
+ array_key_exists('count', $selectedCondition),
+ $selectedCondition['value'],
+ (!is_null($achievementIndex)) ? $achievements[$achievementIndex]['id'] : null,
+ (!empty($selectedCondition['groupby'])) ? $selectedCondition['groupby'] : null
+ );
+ }
+ break;
+ }
+ }
+
+ // Add new condition
+ if(array_key_exists('new', $selectedConditions))
+ {
+ $condition = $selectedConditions['new'];
+ switch($achievement['condition'])
+ {
+ // Date conditions
+ case 'date':
+ if(!empty($condition['select'])) {
+ $this->Achievements->addAchievementConditionDate(
+ $this->Auth->getUserId(),
+ $achievement['id'],
+ $condition['select']
+ );
+ }
+ break;
+ // Character conditions
+ case 'character':
+ if(!empty($condition['value'])) {
+ $this->Achievements->addAchievementConditionCharacter(
+ $this->Auth->getUserId(),
+ $achievement['id'],
+ $condition['field'],
+ $condition['value']
+ );
+ }
+ break;
+ // Quest conditions
+ case 'quest':
+ if(!empty($condition['value']))
+ {
+ // Get selected Quest
+ $questIndex = null;
+ if(!empty($condition['quest'])) {
+ foreach($quests as $index => &$quest) {
+ if($quest['url'] == $condition['quest']) {
+ $questIndex = $index;
+ }
+ }
+ }
+
+ // Edit condition
+ $this->Achievements->addAchievementConditionQuest(
+ $this->Auth->getUserId(),
+ $achievement['id'],
+ $condition['field'],
+ array_key_exists('count', $condition),
+ $condition['value'],
+ (!is_null($questIndex)) ? $quests[$questIndex]['id'] : null,
+ ($condition['status'] != '') ? $condition['status'] : null,
+ (!empty($condition['groupby'])) ? $condition['groupby'] : null
+ );
+ }
+ break;
+ // Achievement conditions
+ case 'achievement':
+ if(!empty($condition['value']))
+ {
+ // Get selected Achievement
+ $achievmentIndex = null;
+ if(!empty($selectedCondition['achievement'])) {
+ foreach($achievements as $index => &$a) {
+ if($a['url'] == $selectedCondition['achievement']) {
+ $achievementIndex = $index;
+ }
+ }
+ }
+
+ // Edit condition
+ $this->Achievements->addAchievementConditionAchievement(
+ $this->Auth->getUserId(),
+ $achievement['id'],
+ $condition['field'],
+ array_key_exists('count', $condition),
+ $condition['value'],
+ (!is_null($achievementIndex)) ? $achievements[$achievementIndex]['id'] : null,
+ (!empty($condition['groupby'])) ? $condition['groupby'] : null
+ );
+ }
+ break;
+ }
+ }
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array(), 4));
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Edit Achievement conditions');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('achievement', $achievement);
+ $this->set('conditions', $conditions);
+ $this->set('characterFields', $characterFields);
+ $this->set('questFields', $questFields);
+ $this->set('quests', $quests);
+ $this->set('achievementFields', $achievementFields);
+ $this->set('achievements', $achievements);
+ $this->set('deletes', $deletes);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete an Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $achievementUrl URL-title of Achievement
+ */
+ public function delete($seminaryUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Achievements->deleteAchievement($achievement);
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('manage', $seminary['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete seminary');
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ $this->set('achievement', $achievement);
+ }
+ }
+
+?>
diff --git a/controllers/AjaxController.inc b/controllers/AjaxController.inc
new file mode 100644
index 00000000..3e824c25
--- /dev/null
+++ b/controllers/AjaxController.inc
@@ -0,0 +1,52 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the AjaxAgent to return a JSON-string used by AJAX.
+ *
+ * @author Oliver Hanraths
+ */
+ class AjaxController extends \hhu\z\Controller
+ {
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Set content-type
+ $this->response->addHeader("Content-type: application/json; charset=utf-8");
+ }
+
+
+ /**
+ * Action: index.
+ *
+ * Create the JSON-string.
+ */
+ public function index()
+ {
+ }
+
+ }
+
+?>
diff --git a/controllers/BinaryController.inc b/controllers/BinaryController.inc
new file mode 100644
index 00000000..9f7d6eda
--- /dev/null
+++ b/controllers/BinaryController.inc
@@ -0,0 +1,37 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the BinaryAgent to show binary data.
+ *
+ * @author Oliver Hanraths
+ */
+ class BinaryController extends \hhu\z\controllers\IntermediateController
+ {
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Create binary data.
+ */
+ public function index()
+ {
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc
new file mode 100644
index 00000000..e01a7241
--- /dev/null
+++ b/controllers/CharactergroupsController.inc
@@ -0,0 +1,762 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the CharactergroupsAgent to display Character groups.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'charactergroups', 'charactergroupsquests', 'characters', 'avatars', 'media');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'groupsgroup' => array('admin', 'moderator', 'user'),
+ 'creategroupsgroup' => array('admin', 'moderator', 'user'),
+ 'editgroupsgroup' => array('admin', 'moderator', 'user'),
+ 'deletegroupsgroup' => array('admin', 'moderator', 'user'),
+ 'group' => array('admin', 'moderator', 'user'),
+ 'managegroup' => array('admin', 'moderator', 'user'),
+ 'creategroup' => array('admin', 'moderator', 'user'),
+ 'editgroup' => array('admin', 'moderator', 'user'),
+ 'deletegroup' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'groupsgroup' => array('admin', 'moderator', 'user'),
+ 'creategroupsgroup' => array('admin', 'moderator'),
+ 'editgroupsgroup' => array('admin', 'moderator'),
+ 'deletegroupsgroup' => array('admin', 'moderator'),
+ 'group' => array('admin', 'moderator', 'user'),
+ 'managegroup' => array('admin', 'moderator'),
+ 'creategroup' => array('admin', 'moderator'),
+ 'editgroup' => array('admin', 'moderator', 'user'),
+ 'deletegroup' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Show Character groups-groups for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function index($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-groups
+ $groupsgroups = $this->Charactergroups->getGroupsroupsForSeminary($seminary['id']);
+
+
+ // Set titile
+ $this->addTitleLocalized('Character Groups');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroups', $groupsgroups);
+ }
+
+
+ /**
+ * Action: groupsgroups.
+ *
+ * Show Character groups for a Character groups-group of a
+ * Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ */
+ public function groupsgroup($seminaryUrl, $groupsgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character groups
+ $groups = $this->Charactergroups->getGroupsForGroupsgroup($groupsgroup['id'], 'xps');
+
+ // Get Character groups-group Quests
+ $quests = $this->Charactergroupsquests->getQuestsForCharactergroupsgroup($groupsgroup['id']);
+
+
+ // Set titile
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitleLocalized('Character Groups');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('groups', $groups);
+ $this->set('quests', $quests);
+ }
+
+
+ /**
+ * Action: creategroupsgroups.
+ *
+ * Create a new Character groups-group for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function creategroupsgroup($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Values
+ $charactergroupsgroupname = '';
+ $preferred = false;
+ $fields = array('charactergroupsgroupname');
+ $validation = array();
+
+ // Create a new Character groups-group
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $charactergroupsgroupname = $this->request->getPostParam('charactergroupsgroupname');
+ if($this->Charactergroups->characterGroupsgroupNameExists($charactergroupsgroupname)) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactergroupsgroupname', 'exist', true);
+ }
+ $preferred = !is_null($this->request->getPostParam('preferred'));
+
+ // Create groups-group
+ if($validation === true)
+ {
+ $groupsgroupId = $this->Charactergroups->createGroupsgroup(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $charactergroupsgroupname,
+ $preferred
+ );
+
+ // Redirect to groups-group page
+ $groupsgroup = $this->Charactergroups->getGroupsgroupById($groupsgroupId);
+ $this->redirect($this->linker->link(array('groupsgroup', $seminary['url'], $groupsgroup['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('New Character groups-group');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('charactergroupsgroupname', $charactergroupsgroupname);
+ $this->set('preferred', $preferred);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: editgroupsgroups.
+ *
+ * Edit a Character groups-group of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ */
+ public function editgroupsgroup($seminaryUrl, $groupsgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Values
+ $charactergroupsgroupname = $groupsgroup['name'];
+ $preferred = $groupsgroup['preferred'];
+ $fields = array('charactergroupsgroupname');
+ $validation = array();
+
+ // Edit Character groups-group
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $charactergroupsgroupname = $this->request->getPostParam('charactergroupsgroupname');
+ if($this->Charactergroups->characterGroupsgroupNameExists($charactergroupsgroupname, $groupsgroup['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactergroupsgroupname', 'exist', true);
+ }
+ $preferred = !is_null($this->request->getPostParam('preferred'));
+
+ // Edit groups-group
+ if($validation === true)
+ {
+ $this->Charactergroups->editGroupsgroup(
+ $groupsgroup['id'],
+ $charactergroupsgroupname,
+ $preferred
+ );
+
+ // Redirect to user page
+ $groupsgroup = $this->Charactergroups->getGroupsgroupById($groupsgroup['id']);
+ $this->redirect($this->linker->link(array('groupsgroup', $seminary['url'], $groupsgroup['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Character groups-group');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('charactergroupsgroupname', $charactergroupsgroupname);
+ $this->set('preferred', $preferred);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: deletegroupsgroups.
+ *
+ * Delete a Character groups-group of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ */
+ public function deletegroupsgroup($seminaryUrl, $groupsgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Charactergroups->deleteGroupsgroup($groupsgroup['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('index', $seminary['url']), 1));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('groupsgroup', $seminary['url'], $groupsgroup['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete Character groups-group');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ }
+
+
+ /**
+ * Action: group.
+ *
+ * Show a Character group for a Character groups-group of a
+ * Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $groupUrl URL-Title of a Character group
+ */
+ public function group($seminaryUrl, $groupsgroupUrl, $groupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character group
+ $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl);
+ $group['characters'] = $this->Characters->getCharactersForGroup($group['id']);
+ $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']);
+
+ // Get Character avatars
+ foreach($group['characters'] as &$character)
+ {
+ $avatar = $this->Avatars->getAvatarById($character['avatar_id']);
+ if(!is_null($avatar['small_avatarpicture_id'])) {
+ $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']);
+ }
+ }
+
+ // Get Character groups Quests
+ $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']);
+
+
+ // Set titile
+ $this->addTitle($group['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('group', $group);
+ $this->set('quests', $quests);
+ }
+
+
+ /**
+ * Action: managegroup.
+ *
+ * Manage a Character group for a Character groups-group of a
+ * Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $groupUrl URL-Title of a Character group
+ */
+ public function managegroup($seminaryUrl, $groupsgroupUrl, $groupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character group
+ $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl);
+
+ // Manage
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('actions')) && count($this->request->getPostParam('actions')) > 0) // && !is_null($this->request->getPostParam('characters')) && count($this->request->getPostParam('characters')) > 0)
+ {
+ $actions = $this->request->getPostParam('actions');
+ $action = array_keys($actions)[0];
+ $selectedCharacters = $this->request->getPostParam('characters');
+
+ switch($action)
+ {
+ // Add Characters to group
+ case 'addcharacters':
+ foreach($selectedCharacters as &$characterId)
+ {
+ $this->Charactergroups->addCharacterToCharactergroup($group['id'], $characterId);
+ // Check Achievements
+ $this->Achievement->checkAchievements($seminary['id'], $characterId, array('character', 'achievement'));
+ }
+ break;
+ // Remove Characters from group
+ case 'removecharacters':
+ foreach($selectedCharacters as &$characterId) {
+ $this->Charactergroups->removeCharacterFromCharactergroup($group['id'], $characterId);
+ }
+ break;
+ }
+ }
+
+ // Get additional data for group
+ $group['characters'] = $this->Characters->getCharactersForGroup($group['id']);
+ $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']);
+
+ // Get Character avatars
+ foreach($group['characters'] as &$character)
+ {
+ $avatar = $this->Avatars->getAvatarById($character['avatar_id']);
+ if(!is_null($avatar['small_avatarpicture_id'])) {
+ $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']);
+ }
+ }
+
+ // Get Character groups Quests
+ $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']);
+
+ // Get all Characters of Seminary
+ $groupCharacterIds = array_map(function($c) { return $c['id']; }, $group['characters']);
+ $seminaryCharacters = $this->Characters->getCharactersForSeminary($seminary['id'], true);
+ $characters = array();
+ foreach($seminaryCharacters as &$character) {
+ if(!in_array($character['id'], $groupCharacterIds)) {
+ $characters[] = $character;
+ }
+ }
+
+
+ // Set titile
+ $this->addTitle($group['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('group', $group);
+ $this->set('quests', $quests);
+ $this->set('characters', $characters);
+ }
+
+
+ /**
+ * Action: creategroup.
+ *
+ * Create a Character group for a Character groups-group of a
+ * Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ */
+ public function creategroup($seminaryUrl, $groupsgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+
+ // Values
+ $charactergroupname = '';
+ $motto = '';
+ $fields = array('charactergroupname', 'motto');
+ $validation = array();
+
+ // Create a new Character groups-group
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $charactergroupname = $this->request->getPostParam('charactergroupname');
+ if($this->Charactergroups->characterGroupNameExists($groupsgroup['id'], $charactergroupname)) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactergroupname', 'exist', true);
+ }
+ $motto = $this->request->getPostParam('motto');
+
+ // Validate icon
+ $icon = null;
+ if(!empty($_FILES) && array_key_exists('icon', $_FILES) && $_FILES['icon']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $icon = $_FILES['icon'];
+
+ // Check error
+ if($icon['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'error', $icon['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $icon['mimetype'] = \hhu\z\Utils::getMimetype($icon['tmp_name'], $icon['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $icon['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'mimetype', $icon['mimetype']);
+ }
+ elseif($icon['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Create group
+ if($validation === true)
+ {
+ $groupId = $this->Charactergroups->createGroup(
+ $this->Auth->getUserId(),
+ $groupsgroup['id'],
+ $charactergroupname,
+ $motto
+ );
+ $group = $this->Charactergroups->getGroupById($groupId);
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createCharactergroupMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroup-%s', $group['url']),
+ '',
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroups->setMediaForGroup($group['id'], $mediaId);
+ }
+ }
+
+ // Redirect to group page
+ $this->redirect($this->linker->link(array('group', $seminary['url'], $groupsgroup['url'], $group['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('New %s Character group', $groupsgroup['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('charactergroupname', $charactergroupname);
+ $this->set('motto', $motto);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: editgroup.
+ *
+ * Edit a Character group for a Character groups-group of a
+ * Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $groupUrl URL-Title of a Character group
+ */
+ public function editgroup($seminaryUrl, $groupsgroupUrl, $groupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character group
+ $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl);
+ $group['characters'] = $this->Characters->getCharactersForGroup($group['id']);
+
+ // Check permission
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) == 0 && !in_array(\hhu\z\controllers\SeminaryController::$character['id'], array_map(function($c) { return $c['id']; }, $group['characters']))) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+
+ // Values
+ $charactergroupname = $group['name'];
+ $motto = $group['motto'];
+ $fields = array('charactergroupname', 'motto');
+ $validation = array();
+
+ // Edit Character group
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $charactergroupname = (count(array_intersect(array('admin','moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) ? $this->request->getPostParam('charactergroupname') : $group['name'];
+ if($this->Charactergroups->characterGroupNameExists($groupsgroup['id'], $charactergroupname, $group['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactergroupname', 'exist', true);
+ }
+ $motto = $this->request->getPostParam('motto');
+
+ // Validate icon
+ $icon = null;
+ if(!empty($_FILES) && array_key_exists('icon', $_FILES) && $_FILES['icon']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $icon = $_FILES['icon'];
+
+ // Check error
+ if($icon['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'error', $icon['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $icon['mimetype'] = \hhu\z\Utils::getMimetype($icon['tmp_name'], $icon['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $icon['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'mimetype', $icon['mimetype']);
+ }
+ elseif($icon['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Edit group
+ if($validation === true)
+ {
+ $this->Charactergroups->editGroup(
+ $group['id'],
+ $charactergroupname,
+ $motto
+ );
+ $group = $this->Charactergroups->getGroupById($group['id']);
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createCharactergroupMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroup-%s', $group['url']),
+ '',
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroups->setMediaForGroup($group['id'], $mediaId);
+ }
+ }
+
+ // Redirect to user page
+ $this->redirect($this->linker->link(array('group', $seminary['url'], $groupsgroup['url'], $group['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Edit %s Character group', $groupsgroup['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('group', $group);
+ $this->set('charactergroupname', $charactergroupname);
+ $this->set('motto', $motto);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: deletegroup.
+ *
+ * Delete a Character group for a Character groups-group of a
+ * Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $groupUrl URL-Title of a Character group
+ */
+ public function deletegroup($seminaryUrl, $groupsgroupUrl, $groupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character group
+ $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Charactergroups->deleteGroup($group['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('groupsgroup', $seminary['url'], $groupsgroup['url']), 1));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('group', $seminary['url'], $groupsgroup['url'], $group['url']), 1));
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Delete %s Character group', $groupsgroup['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('group', $group);
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc
new file mode 100644
index 00000000..21c1778e
--- /dev/null
+++ b/controllers/CharactergroupsquestsController.inc
@@ -0,0 +1,637 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the CharactergroupsquestsAgent to display Character
+ * groups Quests.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsquestsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'charactergroups', 'charactergroupsquests', 'media', 'questgroups', 'uploads');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'quest' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'quest' => array('admin', 'moderator', 'user')
+ );
+
+
+
+
+ /**
+ * Action: quest.
+ *
+ * Show a Character groups Quest for a Character groups-group
+ * of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $questUrl URL-Title of a Character groups Quest
+ */
+ public function quest($seminaryUrl, $groupsgroupUrl, $questUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character groups-group Quests
+ $quest = $this->Charactergroupsquests->getQuestByUrl($groupsgroup['id'], $questUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupById($quest['questgroups_id']);
+ $questgroup['entered'] = $this->Questgroups->hasCharacterEnteredQuestgroup($questgroup['id'], self::$character['id']);
+
+ // Get Character groups-groups
+ $groups = $this->Charactergroups->getGroupsForQuest($quest['id']);
+
+ // Get uploads
+ $uploads = $this->Charactergroupsquests->getMediaForQuest($quest['id']);
+ foreach($uploads as &$upload) {
+ $upload['upload'] = $this->Uploads->getSeminaryuploadById($upload['seminaryupload_id']);
+ }
+
+
+ // Set title
+ $this->addTitle($quest['title']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('quest', $quest);
+ $this->set('questgroup', $questgroup);
+ $this->set('groups', $groups);
+ $this->set('uploads', $uploads);
+ }
+
+
+ /**
+ * Action: manage.
+ *
+ * Manage a Character groups Quest for a Character groups-group
+ * of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $questUrl URL-Title of a Character groups Quest
+ */
+ public function manage($seminaryUrl, $groupsgroupUrl, $questUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character groups-group Quests
+ $quest = $this->Charactergroupsquests->getQuestByUrl($groupsgroup['id'], $questUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupById($quest['questgroups_id']);
+ $questgroup['entered'] = $this->Questgroups->hasCharacterEnteredQuestgroup($questgroup['id'], self::$character['id']);
+
+ // Get Character groups
+ $groups = $this->Charactergroups->getGroupsForGroupsgroup($groupsgroup['id']);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['charactergroupsquests'];
+
+ // Manage
+ $validation = array();
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Upload media
+ if(!is_null($this->request->getPostParam('setmedia')))
+ {
+ $file = $_FILES['media'];
+
+ // Check error
+ if($file['error'] !== 0 || empty($file['tmp_name'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'error', $file['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $file['mimetype'] = \hhu\z\Utils::getMimetype($file['tmp_name'], $file['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $file['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'mimetype', $file['mimetype']);
+ }
+ elseif($file['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'size', $mediaMimetype['size']);
+ }
+
+ // Upload media
+ if($validation === true || empty($valiadion))
+ {
+ // Create filename
+ $filename = sprintf(
+ '%s-%d-%s.%s',
+ 'charactergroupsquest',
+ $quest['id'],
+ date('Ymd-His'),
+ mb_substr($file['name'], strrpos($file['name'], '.')+1)
+ );
+
+ // Upload file
+ $this->Charactergroupsquests->uploadMediaForQuest($this->Auth->getUserId(), $seminary['id'], $quest['id'], $file, $filename);
+ }
+ }
+
+ // Delete media
+ if(!is_null($this->request->getPostParam('deletemedia')))
+ {
+ $uploadIds = $this->request->getPostParam('uploads');
+ var_dump($uploadIds);
+ foreach($uploadIds as $uploadId) {
+ $this->Uploads->deleteSeminaryupload($uploadId);
+ }
+ }
+
+ // Set XPs of Character groups for this Character groups Quest
+ if(!is_null($this->request->getPostParam('setxps')))
+ {
+ $xps = $this->request->getPostParam('xps');
+ foreach($groups as &$group)
+ {
+ // Set XPs
+ if(array_key_exists($group['url'], $xps) && $xps[$group['url']] != 'null')
+ {
+ $xpsFactor = intval($xps[$group['url']]) / $quest['xps'];
+ $this->Charactergroupsquests->setXPsOfGroupForQuest($quest['id'], $group['id'], $xpsFactor);
+ }
+ else {
+ $this->Charactergroupsquests->deleteGroupForQuest($quest['id'], $group['id']);
+ }
+
+ // Check Achievements of Characters
+ $characters = $this->Characters->getCharactersForGroup($group['id']);
+ foreach($characters as &$character)
+ {
+ $this->Achievement->checkAchievements(
+ $seminary['id'],
+ $character['id'],
+ array('character', 'achievement')
+ );
+ }
+ }
+ }
+
+ // Redirect to Quest page
+ if($validation === true || empty($validation)) {
+ $this->redirect($this->linker->link(array('quest', $seminary['url'], $groupsgroup['url'], $quest['url']), 1));
+ }
+ }
+
+ // Get icon
+ $questmedia = null;
+ if(!is_null($quest['questsmedia_id'])) {
+ $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']);
+ }
+
+ // Get uploads
+ $uploads = $this->Charactergroupsquests->getMediaForQuest($quest['id']);
+ foreach($uploads as &$upload) {
+ $upload['upload'] = $this->Uploads->getSeminaryuploadById($upload['seminaryupload_id']);
+ }
+
+
+ // Set XPs for Groups
+ foreach($groups as &$group) {
+ $group['quest_group'] = $this->Charactergroupsquests->getXPsOfGroupForQuest($quest['id'], $group['id']);
+ }
+
+
+ // Set title
+ $this->addTitle($quest['title']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('quest', $quest);
+ $this->set('uploads', $uploads);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('questgroup', $questgroup);
+ $this->set('groups', $groups);
+ $this->set('media', $questmedia);
+ $this->set('validation', $validation);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Character groups Quest for a Character
+ * groups-group of a Seminary.
+ *
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ */
+ public function create($seminaryUrl, $groupsgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Questgroups
+ $questgroups = $this->Questgroups->getQuestgroupsForSeminary($seminary['id']);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+
+ // Values
+ $title = '';
+ $xps = 0;
+ $description = '';
+ $rules = '';
+ $wonText = '';
+ $lostText = '';
+ $fields = array('title', 'xps');
+ $validation = array();
+
+ // Create a new Character groups Quest
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Charactergroupsquests->characterGroupsQuestTitleExists($groupsgroup['id'], $title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $xps = $this->request->getPostParam('xps');
+ $description = $this->request->getPostParam('description');
+ $rules = $this->request->getPostParam('rules');
+ $wonText = $this->request->getPostParam('wonText');
+ $lostText = $this->request->getPostParam('lostText');
+
+ // Validate icon
+ $icon = null;
+ if(!empty($_FILES) && array_key_exists('icon', $_FILES) && $_FILES['icon']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $icon = $_FILES['icon'];
+
+ // Check error
+ if($icon['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'error', $icon['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $icon['mimetype'] = \hhu\z\Utils::getMimetype($icon['tmp_name'], $icon['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $icon['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'mimetype', $icon['mimetype']);
+ }
+ elseif($icon['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Validate Questgroup
+ $questgroupIndex = null;
+ foreach($questgroups as $index => &$questgroup)
+ {
+ $questgroup['selected'] = ($questgroup['url'] == $this->request->getPostParam('questgroup'));
+ if($questgroup['selected']) {
+ $questgroupIndex = $index;
+ }
+ }
+ if(is_null($questgroupIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($questgroup);
+ }
+
+ // Create groups Quest
+ if($validation === true)
+ {
+ $questId = $this->Charactergroupsquests->createQuest(
+ $this->Auth->getUserId(),
+ $groupsgroup['id'],
+ $questgroups[$questgroupIndex]['id'],
+ $title,
+ $description,
+ $xps,
+ $rules,
+ $wonText,
+ $lostText
+ );
+ $quest = $this->Charactergroupsquests->getQuestById($questId);
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroupsquest-%s', $quest['url']),
+ '',
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroupsquests->setMediaForQuest($quest['id'], $mediaId);
+ }
+ }
+
+ // Redirect to Quest page
+ $this->redirect($this->linker->link(array('quest', $seminary['url'], $groupsgroup['url'], $quest['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('New %s-Quest', $groupsgroup['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('questgroups', $questgroups);
+ $this->set('title', $title);
+ $this->set('xps', $xps);
+ $this->set('description', $description);
+ $this->set('rules', $rules);
+ $this->set('wonText', $wonText);
+ $this->set('lostText', $lostText);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a Character groups Quest of a Character groups-group
+ * of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $questUrl URL-Title of a Character groups Quest
+ */
+ public function edit($seminaryUrl, $groupsgroupUrl, $questUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character groups-group Quests
+ $quest = $this->Charactergroupsquests->getQuestByUrl($groupsgroup['id'], $questUrl);
+
+ // Get Questgroups
+ $questgroups = $this->Questgroups->getQuestgroupsForSeminary($seminary['id']);
+ foreach($questgroups as $index => &$questgroup) {
+ $questgroup['selected'] = ($questgroup['id'] == $quest['questgroups_id']);
+ }
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+
+ // Values
+ $title = $quest['title'];
+ $xps = $quest['xps'];
+ $description = $quest['description'];
+ $rules = $quest['rules'];
+ $wonText = $quest['won_text'];
+ $lostText = $quest['lost_text'];
+ $fields = array('title', 'xps');
+ $validation = array();
+
+ // Edit Character group
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Charactergroupsquests->characterGroupsQuestTitleExists($groupsgroup['id'], $title, $quest['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $xps = $this->request->getPostParam('xps');
+ $description = $this->request->getPostParam('description');
+ $rules = $this->request->getPostParam('rules');
+ $wonText = $this->request->getPostParam('wonText');
+ $lostText = $this->request->getPostParam('lostText');
+
+ // Validate icon
+ $icon = null;
+ if(!empty($_FILES) && array_key_exists('icon', $_FILES) && $_FILES['icon']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $icon = $_FILES['icon'];
+
+ // Check error
+ if($icon['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'error', $icon['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $icon['mimetype'] = \hhu\z\Utils::getMimetype($icon['tmp_name'], $icon['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $icon['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'mimetype', $icon['mimetype']);
+ }
+ elseif($icon['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'icon', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Validate Questgroup
+ $questgroupIndex = null;
+ foreach($questgroups as $index => &$questgroup)
+ {
+ $questgroup['selected'] = ($questgroup['url'] == $this->request->getPostParam('questgroup'));
+ if($questgroup['selected']) {
+ $questgroupIndex = $index;
+ }
+ }
+ if(is_null($questgroupIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($questgroup);
+ }
+
+ // Edit groups Quest
+ if($validation === true)
+ {
+ $this->Charactergroupsquests->editQuest(
+ $quest['id'],
+ $groupsgroup['id'],
+ $questgroups[$questgroupIndex]['id'],
+ $title,
+ $description,
+ $xps,
+ $rules,
+ $wonText,
+ $lostText
+ );
+ $quest = $this->Charactergroupsquests->getQuestById($quest['id']);
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroupsquest-%s', $quest['url']),
+ '',
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroupsquests->setMediaForQuest($quest['id'], $mediaId);
+ }
+ }
+
+ // Redirect to Quest page
+ $this->redirect($this->linker->link(array('quest', $seminary['url'], $groupsgroup['url'], $quest['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Edit %s-Quest', $groupsgroup['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('quest', $quest);
+ $this->set('questgroups', $questgroups);
+ $this->set('title', $title);
+ $this->set('xps', $xps);
+ $this->set('description', $description);
+ $this->set('rules', $rules);
+ $this->set('wonText', $wonText);
+ $this->set('lostText', $lostText);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Character groups Quest of a Character groups-group
+ * of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $questUrl URL-Title of a Character groups Quest
+ */
+ public function delete($seminaryUrl, $groupsgroupUrl, $questUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character groups-group Quests
+ $quest = $this->Charactergroupsquests->getQuestByUrl($groupsgroup['id'], $questUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Charactergroupsquests->deleteQuest($quest['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('charactergroups', 'groupsgroup', $seminary['url'], $groupsgroup['url'])));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('quest', $seminary['url'], $groupsgroup['url'], $quest['url']), 1));
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Delete %s-Quest', $groupsgroup['name']);
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('quest', $quest);
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc
new file mode 100644
index 00000000..a1e0cc90
--- /dev/null
+++ b/controllers/CharactersController.inc
@@ -0,0 +1,705 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to list registered users and their data.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactersController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media', 'quests', 'questgroups', 'questtopics', 'xplevels');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'character' => array('admin', 'moderator', 'user'),
+ 'register' => array('admin', 'moderator', 'user'),
+ 'manage' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator'),
+ 'character' => array('admin', 'moderator', 'user'),
+ 'manage' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List registered Characters for a Seminary
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $all Whether to list all Characters at once or not (optional)
+ */
+ public function index($seminaryUrl, $all=null)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Seminarycharacterfields
+ $characterfields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']);
+
+ // Set sort order and page
+ $sortorder = 'name';
+ $charactername = null;
+ $page = 1;
+ if($this->request->getRequestMethod() == 'GET')
+ {
+ $sortorder = $this->request->getGetParam('sortorder');
+ $sortorder = !empty($sortorder) ? $sortorder : 'name';
+ $charactername = (!is_null($this->request->getGetParam('charactername'))) ? $this->request->getGetParam('charactername') : $charactername;
+ $page = $this->request->getGetParam('page');
+ $page = !empty($page) ? intval($page) : 1;
+ }
+
+ // Get registered Characters
+ $limit = ($all != 'all') ? \nre\configs\AppConfig::$misc['lists_limit'] : null;
+ $offset = ($all != 'all') ? max((intval($page) - 1), 0) * $limit : 0;
+ $charactersCount = $this->Characters->getCharactersForSeminaryCount($seminary['id'], $charactername);
+ $characters = $this->Characters->getCharactersForSeminarySorted($seminary['id'], $sortorder, $charactername, $limit, $offset);
+ foreach($characters as &$character)
+ {
+ $character['user'] = $this->Users->getUserById($character['user_id']);
+ $character['characterroles'] = array_map(function($r) { return $r['name']; }, $this->Characterroles->getCharacterrolesForCharacterById($character['id']));
+ $character['characterfields'] = array();
+ foreach($this->Seminarycharacterfields->getFieldsForCharacter($character['id']) as $value) {
+ $character['characterfields'][$value['url']] = $value;
+ }
+ try {
+ $character['xplevel'] = $this->Xplevels->getXPLevelById($character['xplevel_id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // No XP-level
+ }
+ try {
+ $character['avatar'] = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $character['charactertype_url'], $character['xplevel']['level']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // No Avatar available
+ }
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Characters');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('characters', $characters);
+ $this->set('charactersCount', $charactersCount);
+ $this->set('characterfields', $characterfields);
+ $this->set('sortorder', $sortorder);
+ $this->set('charactername', $charactername);
+ $this->set('all', $all);
+ $this->set('page', $page);
+ $this->set('limit', $limit);
+ }
+
+
+ /**
+ * Action: character.
+ *
+ * Show a Charater and its details.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $characterUrl URL-name of a Charater
+ */
+ public function character($seminaryUrl, $characterUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+ $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']);
+
+ // Get Character
+ $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl);
+ try {
+ $character['xplevel'] = $this->Xplevels->getXPLevelById($character['xplevel_id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // No XP-level
+ }
+ $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']);
+ if(!is_null($character['avatar_id'])) {
+ $character['avatar'] = $this->Avatars->getAvatarById($character['avatar_id']);
+ }
+
+ // Get User
+ $user = $this->Users->getUserById($character['user_id']);
+
+ // Get Character groups
+ $groups = $this->Charactergroups->getGroupsForCharacter($character['id']);
+ foreach($groups as &$group) {
+ $group['groupsgroup'] = $this->Charactergroups->getGroupsgroupById($group['charactergroupsgroup_id']);
+ }
+
+ // Get Achievements
+ $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']);
+
+ // Get Achievements with deadline (milestones)
+ $milestones = $this->Achievements->getDeadlineAchievements($seminary['id']);
+ foreach($milestones as &$milestone) {
+ $milestone['achieved'] = $this->Achievements->hasCharacterAchievedAchievement($milestone['id'], $character['id']);
+ }
+
+ // Get ranking
+ $ranking = array(
+ 'superior' => $this->Characters->getSuperiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']),
+ 'inferior' => $this->Characters->getInferiorCharacters($seminary['id'], $character['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range'])
+ );
+
+ // Get Quest topics
+ $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']);
+ foreach($questtopics as &$questtopic)
+ {
+ $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']);
+ $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']);
+ }
+
+ // Get “last” Quest
+ $lastQuest = null;
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0)
+ {
+ $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']);
+ if(!is_null($lastQuest)) {
+ $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']);
+ }
+ }
+
+
+ // Set titile
+ $this->addTitle($character['name']);
+ $this->addTitleLocalized('Characters');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('character', $character);
+ $this->set('user', $user);
+ $this->set('groups', $groups);
+ $this->set('achievements', $achievements);
+ $this->set('milestones', $milestones);
+ $this->set('ranking', $ranking);
+ $this->set('questtopics', $questtopics);
+ $this->set('lastQuest', $lastQuest);
+ }
+
+
+ /**
+ * Acton: register.
+ *
+ * Register a new character for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @throws \nre\exceptions\ParamsNotValidException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function register($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check for already existing Character
+ try {
+ $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // This should be the case
+ }
+
+
+ // Character types
+ $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']);
+
+ // Character fields
+ $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']);
+
+ // Register Character
+ $charactername = '';
+ $validation = true;
+ $fieldsValidation = true;
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Validate Character properties
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername'));
+ $charactername = $this->request->getPostParam('charactername');
+ if($this->Characters->characterNameExists($charactername)) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactername', 'exist', true);
+ }
+
+ // Validate type
+ $typeIndex = null;
+ foreach($types as $index => &$type)
+ {
+ $type['selected'] = ($type['url'] == $this->request->getPostParam('type'));
+ if($type['selected']) {
+ $typeIndex = $index;
+ }
+ }
+ if(is_null($typeIndex)) {
+ $validation = $this->Validation->addValidationResult($validation, 'type', 'exist', false);
+ }
+
+ // Validate fields
+ $fieldsValues = $this->request->getPostParam('fields');
+ foreach($fields as &$field)
+ {
+ if(!array_key_exists($field['url'], $fieldsValues)) {
+ throw new \nre\exceptions\ParamsNotValidException($index);
+ }
+ $field['uservalue'] = $fieldsValues[$field['url']];
+ if($field['required'])
+ {
+ $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex']));
+ if($fieldValidation !== true)
+ {
+ if(!is_array($fieldsValidation)) {
+ $fieldsValidation = array();
+ }
+ $fieldsValidation[$field['url']] = $fieldValidation;
+ }
+ }
+ }
+
+ // Register
+ if($validation === true && $fieldsValidation === true)
+ {
+ $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername);
+ $character = $this->Characters->getCharacterById($characterId);
+
+ // Add Seminary fields
+ foreach($fields as &$field) {
+ if(!empty($fieldsValues[$field['url']])) {
+ $this->Seminarycharacterfields->setSeminaryFieldOfCharacter($field['id'], $characterId, $fieldsValues[$field['url']]);
+ }
+ }
+
+ // Set roles for owners and admins
+ if(in_array('admin', \hhu\z\controllers\IntermediateController::$user['roles']) || $seminary['created_user_id'] == \hhu\z\controllers\IntermediateController::$user['id']) {
+ $this->Characterroles->addCharacterroleToCharacter($characterId, 'admin');
+ }
+
+ // Send mail
+ $this->sendRegistrationMail($character);
+
+ // Redirect
+ $this->redirect($this->linker->link(array('seminaries')));
+ }
+ }
+
+ // Get XP-levels
+ $xplevels = $this->Xplevels->getXPLevelsForSeminary($seminary['id']);
+
+ // Get Avatars
+ if(count($xplevels) > 0)
+ {
+ foreach($types as &$type)
+ {
+ try {
+ $type['avatar'] = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $type['url'], $xplevels[0]['level']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // No Avatar available
+ }
+ }
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Create Character');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('types', $types);
+ $this->set('fields', $fields);
+ $this->set('charactername', $charactername);
+ $this->set('validation', $validation);
+ $this->set('fieldsValidation', $fieldsValidation);
+ $this->set('xplevels', $xplevels);
+ }
+
+
+ /**
+ * Action: manage.
+ *
+ * Manage Characters.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function manage($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Set sort order and page
+ $selectedCharacters = array();
+ $sortorder = 'name';
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Set sortorder
+ $sortorder = $this->request->getPostParam('sortorder');
+ $sortorder = !empty($sortorder) ? $sortorder : 'name';
+
+ // Do action
+ $selectedCharacters = $this->request->getPostParam('characters');
+ if(!is_array($selectedCharacters)) {
+ $selectedCharacters = array();
+ }
+ if(!is_null($this->request->getPostParam('actions')) && count($this->request->getPostParam('actions')) > 0 && !is_null($this->request->getPostParam('characters')) && count($this->request->getPostParam('characters')) > 0)
+ {
+ $actions = $this->request->getPostParam('actions');
+ $action = array_keys($actions)[0];
+
+ switch($action)
+ {
+ // Add/remove role to/from Characters
+ case 'addrole':
+ case 'removerole':
+ // Determine role and check permissions
+ $role = null;
+ switch($actions[$action])
+ {
+ case _('Admin'):
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) <= 0 && !in_array('admin', \hhu\z\controllers\SeminaryController::$character['characterroles'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ $role = 'admin';
+ break;
+ case _('Moderator'):
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) <= 0 && !in_array('admin', \hhu\z\controllers\SeminaryController::$character['characterroles'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ $role = 'moderator';
+ break;
+ case _('User'):
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) <= 0 && count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) <= 0) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ $role = 'user';
+ break;
+ }
+
+ // Add role
+ if($action == 'addrole') {
+ foreach($selectedCharacters as &$characterId) {
+ $this->Characterroles->addCharacterroleToCharacter($characterId, $role);
+ }
+ }
+ // Remove role
+ else {
+ foreach($selectedCharacters as &$characterId) {
+ $this->Characterroles->removeCharacterroleFromCharacter($characterId, $role);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // Get Seminarycharacterfields
+ $characterfields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']);
+
+ // Get registered Characters
+ $characters = $this->Characters->getCharactersForSeminarySorted($seminary['id'], $sortorder);
+ foreach($characters as &$character)
+ {
+ try {
+ $character['xplevel'] = $this->Xplevels->getXPLevelById($character['xplevel_id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // No XP-level
+ }
+ if(!is_null($character['avatar_id'])) {
+ $character['avatar'] = $this->Avatars->getAvatarById($character['avatar_id']);
+ }
+ $character['user'] = $this->Users->getUserById($character['user_id']);
+ $character['characterroles'] = array_map(function($r) { return $r['name']; }, $this->Characterroles->getCharacterrolesForCharacterById($character['id']));
+ $character['characterfields'] = array();
+ foreach($this->Seminarycharacterfields->getFieldsForCharacter($character['id']) as $value) {
+ $character['characterfields'][$value['url']] = $value;
+ }
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Manage Characters');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('characters', $characters);
+ $this->set('characterfields', $characterfields);
+ $this->set('selectedCharacters', $selectedCharacters);
+ $this->set('sortorder', $sortorder);
+ }
+
+
+ /**
+ * Acton: edit.
+ *
+ * Edit a new character for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @throws \nre\exceptions\ParamsNotValidException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $characterUrl URL-name of a Charater
+ */
+ public function edit($seminaryUrl, $characterUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character
+ $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl);
+
+ // Check permissions
+ if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) == 0 && $character['id'] != \hhu\z\controllers\SeminaryController::$character['id']) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Get User
+ $user = $this->Users->getUserById($character['user_id']);
+
+ // Character types
+ $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']);
+ foreach($types as &$type) {
+ $type['selected'] = ($type['url'] == $character['charactertype_url']);
+ }
+
+ // Character fields
+ $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']);
+ foreach($fields as &$field)
+ {
+ $userValue = $this->Seminarycharacterfields->getSeminaryFieldOfCharacter($field['id'], $character['id']);
+ if(!empty($userValue)) {
+ $field['uservalue'] = $userValue['value'];
+ }
+ }
+
+ // Values
+ $charactername = $character['name'];
+ $validation = array();
+ $fieldsValidation = true;
+
+ // Edit Character
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Validate Character properties
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername'));
+ $charactername = (count(array_intersect(array('admin','moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) ? $this->request->getPostParam('charactername') : $character['name'];
+ if($this->Characters->characterNameExists($charactername, $character['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactername', 'exist', true);
+ }
+
+ // Validate type
+ $typeIndex = null;
+ foreach($types as $index => &$type)
+ {
+ $type['selected'] = (count(array_intersect(array('admin','moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) ? ($type['url'] == $this->request->getPostParam('type')) : ($type['url'] == $character['charactertype_url']);
+ if($type['selected']) {
+ $typeIndex = $index;
+ }
+ }
+ if(is_null($typeIndex)) {
+ $validation = $this->Validation->addValidationResult($validation, 'type', 'exist', false);
+ }
+
+ // Validate fields
+ $fieldsValues = $this->request->getPostParam('fields');
+ foreach($fields as &$field)
+ {
+ if(!array_key_exists($field['url'], $fieldsValues)) {
+ throw new \nre\exceptions\ParamsNotValidException($index);
+ }
+ $field['uservalue'] = $fieldsValues[$field['url']];
+ if($field['required'])
+ {
+ $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex']));
+ if($fieldValidation !== true)
+ {
+ if(!is_array($fieldsValidation)) {
+ $fieldsValidation = array();
+ }
+ $fieldsValidation[$field['url']] = $fieldValidation;
+ }
+ }
+ }
+
+ // Edit
+ if($validation === true && $fieldsValidation === true)
+ {
+ $this->Characters->editCharacter(
+ $character['id'],
+ $types[$typeIndex]['id'],
+ $charactername
+ );
+
+ // Set Seminary fields
+ foreach($fields as &$field) {
+ if(!empty($fieldsValues[$field['url']])) {
+ $this->Seminarycharacterfields->setSeminaryFieldOfCharacter($field['id'], $character['id'], $fieldsValues[$field['url']]);
+ }
+ }
+
+ // Redirect
+ $character = $this->Characters->getCharacterById($character['id']);
+ $this->redirect($this->linker->link(array('character', $seminary['url'], $character['url']), 1));
+ }
+ }
+
+ // Get XP-levels
+ $xplevels = $this->Xplevels->getXPLevelsForSeminary($seminary['id']);
+
+ // Get validation settings
+ $validationSettings = array();
+ $validationSettings['charactername'] = \nre\configs\AppConfig::$validation['charactername'];
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Character');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('types', $types);
+ $this->set('fields', $fields);
+ $this->set('charactername', $charactername);
+ $this->set('validation', $validation);
+ $this->set('fieldsValidation', $fieldsValidation);
+ $this->set('validationSettings', $validationSettings);
+ $this->set('xplevels', $xplevels);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Character.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $characterUrl URL-name of a Charater
+ */
+ public function delete($seminaryUrl, $characterUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character
+ $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl);
+
+ // Get User
+ $user = $this->Users->getUserById($character['user_id']);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete Character
+ $this->Characters->deleteCharacter($character['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('index', $seminary['url']), 1));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('index', $seminary['url'], $character['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete Character');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('character', $character);
+ $this->set('user', $user);
+ }
+
+
+
+
+ /**
+ * Send mail for new Character registration.
+ *
+ * @param arary $newCharacter Newly registered Character
+ */
+ private function sendRegistrationMail($newCharacter)
+ {
+ // Get Seminary moderators
+ $characters = $this->Characters->getCharactersWithCharacterRole(self::$seminary['id'], 'moderator');
+
+ // Send notification mail
+ try {
+ foreach($characters as &$character)
+ {
+ $moderator = $this->Users->getUserById($character['user_id']);
+ if($moderator['mailing']) {
+ \hhu\z\Utils::sendMail(
+ $moderator['email'],
+ 'characterregistration',
+ true,
+ array(
+ $moderator,
+ \hhu\z\controllers\SeminaryController::$seminary,
+ \hhu\z\controllers\IntermediateController::$user,
+ $newCharacter
+ ),
+ $this->linker
+ );
+ }
+ }
+ }
+ catch(\hhu\z\exceptions\MailingException $e) {
+ $this->log($e->getMessage());
+ }
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactertypesController.inc b/controllers/CharactertypesController.inc
new file mode 100644
index 00000000..26610baa
--- /dev/null
+++ b/controllers/CharactertypesController.inc
@@ -0,0 +1,406 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the CharactertypesAgent to handle Charactertyes of a
+ * Seminary.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactertypesController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('charactertypes', 'xplevels', 'avatars', 'media');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List Character types.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function index($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check permissions
+ if(
+ (is_null(self::$character) && count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0) &&
+ $seminary['created_user_id'] != self::$user['id']
+ ) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Get Character types
+ $charactertypes = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']);
+
+ // Get Avatars
+ $xplevels = $this->Xplevels->getXPLevelsForSeminary($seminary['id']);
+ if(count($xplevels) > 0)
+ {
+ foreach($charactertypes as &$type)
+ {
+ try {
+ $type['avatar'] = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $type['url'], $xplevels[0]['level']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // No Avatar available
+ }
+ }
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Charactertypes');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('xplevels', $xplevels);
+ $this->set('charactertypes', $charactertypes);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create new Character type for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function create($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check permissions
+ if(
+ $seminary['created_user_id'] != self::$user['id'] &&
+ (is_null(self::$character) && count(array_intersect(array('admin'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0)
+ ) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Values
+ $name = '';
+ $fields = array('charactertypename');
+ $validation = array();
+
+ // Create new Charactertype
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $name = $this->request->getPostParam('charactertypename');
+ if($this->Charactertypes->charactertypeNameExists($seminary['id'], $name)) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactertypename', 'exist', true);
+ }
+
+ // Create new Charactertype
+ if($validation === true)
+ {
+ $charactertypeId = $this->Charactertypes->createCharactertype(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $name
+ );
+ $charactertype = $this->Charactertypes->getCharactertypeById($charactertypeId);
+
+ // Redirect to editing
+ $this->redirect($this->linker->link(array('edit', $seminary['url'], $charactertype['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Create new Charactertype');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('name', $name);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit Character type for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of a Seminary
+ * @param string $charactertypeUrl URL-title of Character type
+ */
+ public function edit($seminaryUrl, $charactertypeUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check permissions
+ if(
+ $seminary['created_user_id'] != self::$user['id'] &&
+ (is_null(self::$character) && count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0)
+ ) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Get Character type
+ $charactertype = $this->Charactertypes->getCharactertypeByUrl($seminary['id'], $charactertypeUrl);
+
+ // XP-levels
+ $xplevels = $this->Xplevels->getXPLevelsForSeminary($seminary['id']);
+ foreach($xplevels as &$xplevel)
+ {
+ try {
+ $xplevel['avatar'] = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $charactertype['url'], $xplevel['level']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ // No Avatar available
+ }
+ }
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $name = $charactertype['name'];
+ $fields = array('charactertypename');
+ $validation = array();
+ $avatarVariants = array('portrait', 'avatar');
+ $avatarsValidation = true;
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $name = $this->request->getPostParam('charactertypename');
+ if($this->Charactertypes->charactertypeNameExists($seminary['id'], $name, $charactertype['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'charactertypename', 'exist', true);
+ }
+
+ // Validate and upload avatars
+ if(array_key_exists('avatars', $_FILES))
+ {
+ foreach($xplevels as &$xplevel)
+ {
+ if(array_key_exists($xplevel['id'], $_FILES['avatars']['error']))
+ {
+ foreach($avatarVariants as &$variant)
+ {
+ if(array_key_exists($variant, $_FILES['avatars']['error'][$xplevel['id']]) && $_FILES['avatars']['error'][$xplevel['id']][$variant] !== UPLOAD_ERR_NO_FILE)
+ {
+ $avatar = array(
+ 'name' => $_FILES['avatars']['name'][$xplevel['id']][$variant],
+ 'type' => $_FILES['avatars']['type'][$xplevel['id']][$variant],
+ 'tmp_name' => $_FILES['avatars']['tmp_name'][$xplevel['id']][$variant],
+ 'error' => $_FILES['avatars']['error'][$xplevel['id']][$variant],
+ 'size' => $_FILES['avatars']['size'][$xplevel['id']][$variant]
+ );
+ $avatarValidation = true;
+
+ // Check error
+ if($avatar['error'] !== UPLOAD_ERR_OK) {
+ $avatarValidation = $this->Validation->addValidationResult($avatarValidation, 'avatar', 'error', $avatar['error']);
+ }
+
+ // Check mimetype
+ $avatarMimetype = null;
+ $avatar['mimetype'] = \hhu\z\Utils::getMimetype($avatar['tmp_name'], $avatar['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $avatar['mimetype']) {
+ $avatarMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($avatarMimetype)) {
+ $avatarValidation = $this->Validation->addValidationResult($avatarValidation, 'avatar', 'mimetype', $avatar['mimetype']);
+ }
+ elseif($avatar['size'] > $avatarMimetype['size']) {
+ $avatarValidation = $this->Validation->addValidationResult($avatarValidation, 'avatar', 'size', $avatarMimetype['size']);
+ }
+
+ // Add validation result
+ if(!$avatarValidation !== true)
+ {
+ if(!is_array($avatarsValidation)) {
+ $avatarsValidation = array();
+ }
+ if(!array_key_exists($xplevel['id'], $avatarsValidation)) {
+ $avatarsValidation[$xplevel['id']] = array();
+ }
+ $avatarsValidation[$xplevel['id']][$variant] = $avatarValidation;
+ }
+
+ // Upload avatar
+ if($avatarValidation === true)
+ {
+ $avatar['media_id'] = $this->Media->createAvatarPicture(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('avatar-%d-%d-%s', $charactertype['id'], $xplevel['id'], $variant),
+ '',
+ $avatar['mimetype'],
+ $avatar['tmp_name']
+ );
+
+ // Set avatar
+ if($variant == 'portrait') {
+ $this->Avatars->setAvatarPortraitForTypeAndLevel(
+ $this->Auth->getUserId(),
+ $charactertype['id'],
+ $xplevel['id'],
+ $avatar['media_id']
+ );
+ }
+ else {
+ $this->Avatars->setAvatarForTypeAndLevel(
+ $this->Auth->getUserId(),
+ $charactertype['id'],
+ $xplevel['id'],
+ $avatar['media_id']
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Edit Charactertype
+ if($validation === true && $avatarsValidation === true)
+ {
+ $this->Charactertypes->editCharactertype(
+ $charactertype['id'],
+ $name
+ );
+ $charactertype = $this->Charactertypes->getCharactertypeById($charactertype['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('index', $seminary['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Charactertype');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('charactertype', $charactertype);
+ $this->set('xplevels', $xplevels);
+ $this->set('name', $name);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('avatarsValidation', $avatarsValidation);
+ $this->set('avatarVariants', $avatarVariants);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete Character type for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of a Seminary
+ * @param string $charactertypeUrl URL-title of Character type
+ */
+ public function delete($seminaryUrl, $charactertypeUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check permissions
+ if(
+ $seminary['created_user_id'] != self::$user['id'] &&
+ (is_null(self::$character) && count(array_intersect(array('admin'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0)
+ ) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Get Character type
+ $charactertype = $this->Charactertypes->getCharactertypeByUrl($seminary['id'], $charactertypeUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete Character type
+ $this->Charactertypes->deleteCharactertype($charactertype['id']);
+ }
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('index', $seminary['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete Charactertype');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('charactertype', $charactertype);
+ }
+
+ }
+
+?>
diff --git a/controllers/ErrorController.inc b/controllers/ErrorController.inc
new file mode 100644
index 00000000..61abb150
--- /dev/null
+++ b/controllers/ErrorController.inc
@@ -0,0 +1,51 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to show an error page.
+ *
+ * @author Oliver Hanraths
+ */
+ class ErrorController extends \hhu\z\Controller
+ {
+
+
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Set HTTP-header and print an error message.
+ *
+ * @param int $httpStatusCode HTTP-statuscode of the error that occurred
+ */
+ public function index($httpStatusCode)
+ {
+ // Set HTTP-header
+ if(!array_key_exists($httpStatusCode, \nre\core\WebUtils::$httpStrings)) {
+ $httpStatusCode = 200;
+ }
+ $this->response->addHeader(\nre\core\WebUtils::getHttpHeader($httpStatusCode));
+
+ // Display statuscode and message
+ $this->set('code', $httpStatusCode);
+ $this->set('string', \nre\core\WebUtils::$httpStrings[$httpStatusCode]);
+ $this->set('userId', $this->Auth->getUserId());
+ }
+
+ }
+
+?>
diff --git a/controllers/FaultController.inc b/controllers/FaultController.inc
new file mode 100644
index 00000000..ba8f29da
--- /dev/null
+++ b/controllers/FaultController.inc
@@ -0,0 +1,37 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to display a toplevel error page.
+ *
+ * @author Oliver Hanraths
+ */
+ class FaultController extends \nre\core\Controller
+ {
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Show the error message.
+ */
+ public function index()
+ {
+ }
+
+ }
+
+?>
diff --git a/controllers/HtmlController.inc b/controllers/HtmlController.inc
new file mode 100644
index 00000000..c1402e9f
--- /dev/null
+++ b/controllers/HtmlController.inc
@@ -0,0 +1,114 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the HtmlAgent to display a HTML-page.
+ *
+ * @author Oliver Hanraths
+ */
+ class HtmlController extends \hhu\z\Controller
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('notification');
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Set content-type
+ $this->response->addHeader("Content-type: text/html; charset=utf-8");
+ }
+
+
+ /**
+ * Postfilter that is executed after running the Controller.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function postFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ // Get title
+ $this->set('title', $this->getTitle());
+ }
+
+
+ /**
+ * Action: index.
+ *
+ * Create the HTML-structure.
+ */
+ public function index()
+ {
+ // Set the name of the current IntermediateAgent as page title
+ $this->set('title', $this->request->getParam(1, 'intermediate'));
+
+ // Set userdata
+ $this->set('loggedUser', IntermediateController::$user);
+ $this->set('loggedSeminary', SeminaryController::$seminary);
+ $this->set('loggedCharacter', SeminaryController::$character);
+
+ // Set notifications
+ $this->set('notifications', $this->Notification->getNotifications());
+
+ // Set some application data
+ $this->set('mailcontact', \nre\configs\AppConfig::$app['mailcontact']);
+ }
+
+
+
+
+ /**
+ * Get title information from IntermediateController if possible
+ * and create a title.
+ *
+ * @return string Title for the current page
+ */
+ private function getTitle()
+ {
+ $title = array();
+
+ // Get title of IntermediateController
+ $intermediateController = $this->agent->getIntermediateAgent()->controller;
+ if($intermediateController instanceof \hhu\z\controllers\IntermediateController) {
+ $title = $intermediateController->getTitle();
+ }
+ if(!is_array($title)) {
+ $title = array($title);
+ }
+
+ // Add application name
+ $title[] = \nre\configs\AppConfig::$app['name'];
+
+
+ // Return title with delimiter
+ return implode(\nre\configs\AppConfig::$misc['title_delimiter'], $title);
+ }
+
+ }
+
+?>
diff --git a/controllers/HtmlmailController.inc b/controllers/HtmlmailController.inc
new file mode 100644
index 00000000..7fa8cf9f
--- /dev/null
+++ b/controllers/HtmlmailController.inc
@@ -0,0 +1,54 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the HtmlmailAgent for generating a HTML-mail message.
+ *
+ * @author Oliver Hanraths
+ */
+ class HtmlmailController extends \nre\core\Controller
+ {
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+
+ // Set linker
+ $this->set('linker', ($request instanceof \hhu\z\requests\MailRequest && !is_null($request->getLinker())) ? $request->getLinker() : null);
+ }
+
+
+ /**
+ * Action: index.
+ *
+ * Create HTML-structure of mail message.
+ */
+ public function index()
+ {
+ $this->set('appname', \nre\configs\AppConfig::$app['name']);
+ }
+
+ }
+
+?>
diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc
new file mode 100644
index 00000000..106edb60
--- /dev/null
+++ b/controllers/IntroductionController.inc
@@ -0,0 +1,95 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to show an introduction page.
+ *
+ * @author Oliver Hanraths
+ */
+ class IntroductionController extends \hhu\z\controllers\IntermediateController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('pages');
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index()
+ {
+ // Get introduction text
+ $page = $this->Pages->getPageByUrl(\hhu\z\models\PagesModel::PAGEURL_INTRODUCTION);
+ $text = (!is_null($page)) ? $page['text'] : null;
+
+
+ // Pass data to view
+ $this->set('userId', $this->Auth->getUserId());
+ $this->set('text', $text);
+ }
+
+
+ /**
+ * Action: edit.
+ */
+ public function edit()
+ {
+ // Get page
+ $page = $this->Pages->getPageByUrl(\hhu\z\models\PagesModel::PAGEURL_INTRODUCTION);
+
+ // Values
+ $text = (!is_null($page)) ? $page['text'] : null;
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ if(!is_null($this->request->getPostParam('edit')))
+ {
+ // Get values
+ $text = $this->request->getPostParam('text');
+
+ // Create page if necessary
+ if(is_null($page)) {
+ $pageId = $this->Pages->createPage(
+ $this->Auth->getUserId(),
+ \hhu\z\models\PagesModel::PAGEURL_INTRODUCTION
+ );
+ $page = $this->Pages->getPageById($pageId);
+ }
+
+ // Save text
+ $this->Pages->editPage(
+ $page['id'],
+ $page['title'],
+ $text
+ );
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array('index'), 1));
+ }
+
+
+ // Pass data to view
+ $this->set('text', $text);
+ }
+
+ }
+
+?>
diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc
new file mode 100644
index 00000000..2d1dbf69
--- /dev/null
+++ b/controllers/LibraryController.inc
@@ -0,0 +1,513 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the LibraryAgent to list Quest topics.
+ *
+ * @author Oliver Hanraths
+ */
+ class LibraryController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('questtopics', 'seminaries', 'quests', 'questgroups');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'topic' => array('admin', 'moderator', 'user'),
+ 'manage' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator', 'user', 'guest'),
+ 'topic' => array('admin', 'moderator', 'user', 'guest'),
+ 'manage' => array('admin', 'moderator'),
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'delete' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List Questtopics of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ */
+ public function index($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character
+ $character = SeminaryController::$character;
+
+ // Get Quest topics
+ $totalQuestcount = 0;
+ $totalCharacterQuestcount = 0;
+ $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']);
+ foreach($questtopics as &$questtopic)
+ {
+ // Get Quest count
+ $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']);
+ $totalQuestcount += $questtopic['questcount'];
+
+ // Get Character progress
+ $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']);
+ $totalCharacterQuestcount += $questtopic['characterQuestcount'];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Library');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('totalQuestcount', $totalQuestcount);
+ $this->set('totalCharacterQuestcount', $totalCharacterQuestcount);
+ $this->set('questtopics', $questtopics);
+ }
+
+
+ /**
+ * Action: topic.
+ *
+ * Show a Questtopic and its Quests with Questsubtopics.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $eminaryUrl URL-Title of Seminary
+ * @param string $questtopicUrl URL-Title of Questtopic
+ */
+ public function topic($seminaryUrl, $questtopicUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character
+ $character = SeminaryController::$character;
+
+ // Get Questtopic
+ $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl);
+ $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']);
+ $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']);
+
+ // Get Quests
+ $quests = array();
+ foreach($this->Quests->getQuestsForQuesttopic($questtopic['id']) as $quest)
+ {
+ if($this->Quests->hasCharacterEnteredQuest($quest['id'], $character['id']) || count(array_intersect(array('admin', 'moderator'), self::$character['characterroles'])) > 0)
+ {
+ // Get Subtopics
+ $quest['subtopics'] = $this->Questtopics->getQuestsubtopicsForQuest($quest['id']);
+
+ $quests[] = $quest;
+ }
+ }
+
+
+ // Set title
+ $this->addTitle($questtopic['title']);
+ $this->addTitleLocalized('Library');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questtopic', $questtopic);
+ $this->set('quests', $quests);
+ }
+
+
+ /**
+ * Action: manage.
+ *
+ * Manage a Questtopic and its Quests with Questsubtopics.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $eminaryUrl URL-Title of Seminary
+ * @param string $questtopicUrl URL-Title of Questtopic
+ */
+ public function manage($seminaryUrl, $questtopicUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questtopic
+ $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl);
+
+ // Get Questsubtopics
+ $questsubtopics = $this->Questtopics->getSubtopicsForQuesttopic($questtopic['id']);
+
+ // Set Questsubtopics for Quests
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('questsubtopics')))
+ {
+ $selectedSubtopics = $this->request->getPostParam('questsubtopics');
+
+ // Set subtopics of Quests
+ $quests = $this->Quests->getQuestsForQuesttopic($questtopic['id']);
+ foreach($quests as &$quest)
+ {
+ $subtopics = (array_key_exists($quest['id'], $selectedSubtopics)) ? $selectedSubtopics[$quest['id']] : array();
+ $this->Questtopics->setQuestsubtopicsForQuest($quest['id'], array_keys($subtopics));
+ }
+
+ // Add Quest
+ $addQuestId = $this->request->getPostParam('addquest');
+ if(!empty($addQuestId))
+ {
+ $subtopics = (array_key_exists('addquest', $selectedSubtopics)) ? $selectedSubtopics['addquest'] : array();
+ if(!empty($subtopics)) {
+ $this->Questtopics->setQuestsubtopicsForQuest($addQuestId, array_keys($subtopics));
+ }
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array('topic', $seminary['url'], $questtopic['url']), 1));
+ }
+
+ // Get Quests
+ $quests = $this->Quests->getQuestsForQuesttopic($questtopic['id']);
+ foreach($quests as &$quest)
+ {
+ // Get Subtopics
+ $quest['subtopics'] = $this->Questtopics->getQuestsubtopicsForQuest($quest['id']);
+ $quest['subtopics'] = array_map(function($t) { return $t['id']; }, $quest['subtopics']);
+ }
+
+ // Get all Quests
+ $allQuests = $this->Quests->getQuestsForSeminary($seminary['id']);
+
+
+ // Set title
+ $this->addTitle($questtopic['title']);
+ $this->addTitleLocalized('Library');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questtopic', $questtopic);
+ $this->set('questsubtopics', $questsubtopics);
+ $this->set('quests', $quests);
+ $this->set('allQuests', $allQuests);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Questtopic for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ */
+ public function create($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Values
+ $title = '';
+ $fields = array('title');
+ $validation = array();
+
+ // Create new Questtopic
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Questtopics->questtopicTitleExists($title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+
+ // Create
+ if($validation === true)
+ {
+ $questtopicId = $this->Questtopics->createQuesttopic(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $title
+ );
+ $questtopic = $this->Questtopics->getQuesttopicById($questtopicId);
+
+ // Redirect to Questtopic
+ $this->redirect($this->linker->link(array('topic', $seminary['url'], $questtopic['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('New Questtopic');
+ $this->addTitleLocalized('Library');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('title', $title);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a Questtopic of a Seminary and its Questsubtopics.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $eminaryUrl URL-Title of Seminary
+ * @param string $questtopicUrl URL-Title of Questtopic
+ */
+ public function edit($seminaryUrl, $questtopicUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questtopic
+ $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl);
+
+ // Get Questsubtopics
+ $questsubtopics = $this->Questtopics->getSubtopicsForQuesttopic($questtopic['id']);
+
+ // Values
+ $questtopicTitle = $questtopic['title'];
+ $subtopicsTitles = array();
+ foreach($questsubtopics as &$questsubtopic) {
+ $subtopicsTitles[$questsubtopic['id']] = $questsubtopic['title'];
+ }
+ $deleteSubtopics = null;
+ $subtopicTitle = '';
+ $validations = array(
+ 'edit' => true,
+ 'edit-subtopics' => true,
+ 'create-subtopic' => true
+ );
+
+ // Edit
+ $action = null;
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Edit Questtopic
+ if(!is_null($this->request->getPostParam('edit')))
+ {
+ $action = 'edit';
+
+ // Get params and validate them
+ $validations[$action] = $this->Validation->validateParams($this->request->getPostParams(), array('title'));
+ $questtopicTitle = $this->request->getPostParam('title');
+ if($this->Questtopics->questsubtopicTitleExists($questtopicTitle, $questtopic['id'])) {
+ $validations[$action] = $this->Validation->addValidationResult($validations[$action], 'title', 'exist', true);
+ }
+
+ // Edit
+ if($validations[$action] === true)
+ {
+ $this->Questtopics->editQuesttopic(
+ $questtopic['id'],
+ $questtopicTitle
+ );
+ $questtopic = $this->Questtopics->getQuesttopicById($questtopic['id']);
+
+ // Redirect
+ $this->redirect($this->linker->link(array('topic', $seminary['url'], $questtopic['url']), 1));
+ }
+ }
+
+ // Edit and delete Questsubtopics
+ elseif(!is_null($this->request->getPostParam('edit-subtopics')))
+ {
+ $action = 'edit-subtopics';
+
+ // Get params and validate them
+ $subtopicsTitles = $this->request->getPostParam('subtopics');
+ $deleteSubtopics = $this->request->getPostParam('delete-subtopics');
+ foreach($questsubtopics as &$questsubtopic)
+ {
+ if(!is_null($deleteSubtopics) && array_key_exists($questsubtopic['id'], $deleteSubtopics)) {
+ continue;
+ }
+
+ $title = $subtopicsTitles[$questsubtopic['id']];
+ $subtopicValidation = $this->Validation->validate($title, \nre\configs\AppConfig::$validation['title']);
+ if($subtopicValidation !== true)
+ {
+ if(!is_array($validations['edit-subtopics'])) {
+ $validations['edit-subtopics'] = array();
+ }
+ if(!array_key_exists($questsubtopic['id'], $validations['edit-subtopics']) || !is_array($validations['edit-subtopics'][$questsubtopic['id']])) {
+ $validations['edit-subtopics'][$questsubtopic['id']] = array();
+ }
+ //$validations['edit-subtopics'][$questsubtopic['id']]['title'] = $subtopicValidation;
+ $validations['edit-subtopics'][$questsubtopic['id']] = $this->Validation->addValidationResults($validations['edit-subtopics'][$questsubtopic['id']], 'title', $subtopicValidation);
+ }
+ if($this->Questtopics->questsubtopicTitleExists($questtopic['id'], $title, $questsubtopic['id']))
+ {
+ if(!is_array($validations['edit-subtopics'])) {
+ $validations['edit-subtopics'] = array();
+ }
+ if(!array_key_exists($questsubtopic['id'], $validations['edit-subtopics']) || !is_array($validations['edit-subtopics'][$questsubtopic['id']])) {
+ $validations['edit-subtopics'][$questsubtopic['id']] = array();
+ }
+ $validations['edit-subtopics'][$questsubtopic['id']] = $this->Validation->addValidationResult($validations['edit-subtopics'][$questsubtopic['id']], 'title', 'exist', true);
+ }
+ }
+
+ // Edit and delete
+ if($validations['edit-subtopics'] === true)
+ {
+ foreach($questsubtopics as &$questsubtopic)
+ {
+ // Delete
+ if(!is_null($deleteSubtopics) && array_key_exists($questsubtopic['id'], $deleteSubtopics)) {
+ $this->Questtopics->deleteQuestsubtopic($questsubtopic['id']);
+ }
+ // Edit
+ elseif(!is_null($subtopicsTitles) && array_key_exists($questsubtopic['id'], $subtopicsTitles))
+ {
+ $title = $subtopicsTitles[$questsubtopic['id']];
+ $this->Questtopics->editQuestsubtopic($questsubtopic['id'], $title);
+ }
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array($seminary['url'], $questtopic['url']), 2));
+ }
+ }
+
+ // Create Questsubtopic
+ elseif(!is_null($this->request->getPostParam('create-subtopic')))
+ {
+ $action = 'create-subtopic';
+
+ // Get params and validate them
+ $validations[$action] = $this->Validation->validateParams($this->request->getPostParams(), array('title'));
+ $subtopicTitle = $this->request->getPostParam('title');
+ if($this->Questtopics->questsubtopicTitleExists($questtopic['id'], $subtopicTitle)) {
+ $validations[$action] = $this->Validation->addValidationResult($validations[$action], 'title', 'exist', true);
+ }
+
+ // Create
+ if($validations[$action] === true)
+ {
+ $this->Questtopics->createQuestsubtopic(
+ $this->Auth->getUserId(),
+ $questtopic['id'],
+ $subtopicTitle
+ );
+ $subtopicTitle = '';
+
+ // Redirect
+ $this->redirect($this->linker->link(null, 4));
+ }
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array(
+ 'title' => \nre\configs\AppConfig::$validation['title']
+ );
+
+
+ // Set title
+ $this->addTitleLocalized('Edit Questtopic');
+ $this->addTitleLocalized('Library');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questtopicTitle', $questtopicTitle);
+ $this->set('subtopicsTitles', $subtopicsTitles);
+ $this->set('deleteSubtopics', $deleteSubtopics);
+ $this->set('subtopicTitle', $subtopicTitle);
+ $this->set('action', $action);
+ $this->set('validations', $validations);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Questtopic of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $eminaryUrl URL-Title of Seminary
+ * @param string $questtopicUrl URL-Title of Questtopic
+ */
+ public function delete($seminaryUrl, $questtopicUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questtopic
+ $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Questtopics->deleteQuesttopic($questtopic['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('index', $seminary['url']), 1));
+ }
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Delete Questtopic');
+ $this->addTitleLocalized('Library');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questtopic', $questtopic);
+ }
+
+ }
+
+?>
diff --git a/controllers/MailController.inc b/controllers/MailController.inc
new file mode 100644
index 00000000..ee8f2c98
--- /dev/null
+++ b/controllers/MailController.inc
@@ -0,0 +1,136 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the MailAgent to generate a mail message.
+ *
+ * @author Oliver Hanraths
+ */
+ class MailController extends \nre\core\Controller
+ {
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+
+ // Set linker
+ $this->set('linker', ($request instanceof \hhu\z\requests\MailRequest && !is_null($request->getLinker())) ? $request->getLinker() : null);
+ }
+
+
+ /**
+ * Action: userregistration.
+ *
+ * Generate a mail message to notify of a new user registration.
+ *
+ * @param array $receiver User that the message will be send to
+ * @param array $neUser Newly registered user
+ */
+ public function userregistration($receiver, $newUser)
+ {
+ // Set subject
+ $this->response->setSubject(_('New user registration'));
+
+
+ // Pass data to view
+ $this->set('user', $newUser);
+ }
+
+
+ /**
+ * Action: characterregistration.
+ *
+ * Generate a mail message to notify of a new Character
+ * registration.
+ *
+ * @param array $receiver User that the message will be send to
+ * @param array $seminary Seminary which the Character was created for
+ * @param arary $user User of the newly registered Character
+ * @param array $newCharacter Newly registered Character
+ */
+ public function characterregistration($receiver, $seminary, $user, $newCharacter)
+ {
+ // Set subject
+ $this->response->setSubject(_('New Character registration'));
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('user', $user);
+ $this->set('character', $newCharacter);
+ }
+
+
+ /**
+ * Action: charactersubmission.
+ *
+ * Generate a mail message to notify of a new Character
+ * submission for a Quest that needs to be valuated.
+ *
+ * @param array $receiver User that the message will be send to
+ * @param array $seminary Seminary which the Quest belongs to
+ * @param array $questgroup Questgroup of Quest
+ * @param array $quest Quest the answer has been submitted for
+ * @param array $character Character that send the submission
+ */
+ public function charactersubmission($receiver, $seminary, $questgroup, $quest, $character)
+ {
+ // Set subject
+ $this->response->setSubject(_('New Character submission'));
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('character', $character);
+ }
+
+
+ /**
+ * Action: charactersubmissionapproved.
+ *
+ * Generate a mail message to notify a Character that its
+ * submission has been approved.
+ *
+ * @param array $receiver User that the message will be send to
+ * @param array $seminary Seminary which the Quest belongs to
+ * @param array $questgroup Questgroup of Quest
+ * @param array $quest Quest the answer has been submitted for
+ */
+ public function charactersubmissionapproved($receiver, $seminary, $questgroup, $quest)
+ {
+ // Set subject
+ $this->response->setSubject(_('Character submission approved'));
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ }
+
+ }
+
+?>
diff --git a/controllers/MailreceiverController.inc b/controllers/MailreceiverController.inc
new file mode 100644
index 00000000..acd8d04c
--- /dev/null
+++ b/controllers/MailreceiverController.inc
@@ -0,0 +1,39 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the MailreceiverAgent to generate a mail receiver
+ * salutation.
+ *
+ * @author Oliver Hanraths
+ */
+ class MailreceiverController extends \nre\core\Controller
+ {
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * @param $array $user User receiving mail
+ */
+ public function index($user)
+ {
+ $this->set('user', $user);
+ }
+
+ }
+
+?>
diff --git a/controllers/MapController.inc b/controllers/MapController.inc
new file mode 100644
index 00000000..e62b6234
--- /dev/null
+++ b/controllers/MapController.inc
@@ -0,0 +1,180 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the MapAgent to display a map.
+ *
+ * @author Oliver Hanraths
+ */
+ class MapController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'map', 'media');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Draw the map.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ */
+ public function index($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get map
+ $map = $this->Map->getMapOfSeminary($seminary['id']);
+
+ // Check permissions
+ if(is_null($map) && count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0) {
+ throw new \nre\exceptions\IdNotFoundException($seminaryUrl);
+ }
+
+ // Set titile
+ $this->addTitleLocalized('Map');
+ $this->addTitle($seminary['title']);
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('map', $map);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit the map of a Seminary.
+ *
+ * @param string $seminaryUrl URL-Title of Seminary
+ */
+ public function edit($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get map
+ $map = $this->Map->getMapOfSeminary($seminary['id']);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['map'];
+
+ // Values
+ $validation = true;
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Validate media (map file)
+ $media = null;
+ if(!empty($_FILES) && array_key_exists('media', $_FILES) && $_FILES['media']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $media = $_FILES['media'];
+
+ // Check error
+ if($media['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'error', $media['error']);
+ }
+ else
+ {
+ // Check mimetype
+ $mediaMimetype = null;
+ $media['mimetype'] = \hhu\z\Utils::getMimetype($media['tmp_name'], $media['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $media['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'mimetype', $media['mimetype']);
+ }
+ elseif($media['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'size', $mediaMimetype['size']);
+ }
+ }
+ }
+
+ // Edit map
+ if($validation === true)
+ {
+ // Update media
+ if(!is_null($media))
+ {
+ $seminarymediaId = $this->Media->createMapMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $media['name'],
+ sprintf('map of %s', $seminary['title']),
+ $media['type'],
+ $media['tmp_name']
+ );
+ if($seminarymediaId > 0) {
+ $this->Map->setMapOfSeminary($seminary['id'], $seminarymediaId);
+ }
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array('index', $seminary['url']), 1));
+ }
+ }
+
+ // Set titile
+ $this->addTitleLocalized('Edit Map');
+ $this->addTitle($seminary['title']);
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('map', $map);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ }
+
+ }
+
+?>
diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc
new file mode 100644
index 00000000..84818c0c
--- /dev/null
+++ b/controllers/MediaController.inc
@@ -0,0 +1,578 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the MediaAgent to process and show Media.
+ *
+ * @author Oliver Hanraths
+ */
+ class MediaController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user', 'guest'),
+ 'seminarymoodpic' => array('admin', 'moderator', 'user'),
+ 'seminarymap' => array('admin', 'moderator', 'user'),
+ 'seminary' => array('admin', 'moderator', 'user'),
+ 'avatar' => array('admin', 'moderator', 'user'),
+ 'achievement' => array('admin', 'moderator', 'user'),
+ 'charactergroup' => array('admin', 'moderator', 'user'),
+ 'charactergroupsquest' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'seminary' => array('admin', 'moderator', 'user', 'guest'),
+ 'achievement' => array('admin', 'moderator', 'user', 'guest'),
+ 'charactergroup' => array('admin', 'moderator', 'user', 'guest'),
+ 'charactergroupsquest' => array('admin', 'moderator', 'user', 'guest')
+ );
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'achievements', 'media', 'avatars', 'charactergroups', 'charactergroupsquests', 'map');
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Set headers for caching control
+ $response->addHeader("Pragma: public");
+ $response->addHeader("Cache-control: must-revalidate");
+ $response->addHeader("Date: ".gmdate(\DateTime::RFC822));
+ }
+
+
+ /**
+ * Action: index
+ *
+ * Display a medium.
+ *
+ * @param string $mediaUrl URL-name of the medium
+ * @param string $action Action for processing the media
+ */
+ public function index($mediaUrl, $action=null)
+ {
+ // Get Media
+ $media = $this->Media->getMediaByUrl($mediaUrl);
+
+ // Get file
+ $file = $this->getMediaFile($media, $action);
+ if(is_null($media)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: seminarymoodpic
+ *
+ * Display the moodpic for a category of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of the Seminary
+ * @param string $category Category to show moodpic of
+ * @param string $action Action for processing the media
+ */
+ public function seminarymoodpic($seminaryUrl, $category=null, $action=null)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Set index
+ switch($category)
+ {
+ case null:
+ $index = 'seminarymedia_id';
+ break;
+ case 'charactergroups':
+ $index = 'charactergroups_seminarymedia_id';
+ break;
+ case 'achievements':
+ $index = 'achievements_seminarymedia_id';
+ break;
+ case 'library':
+ $index = 'library_seminarymedia_id';
+ break;
+ case 'map':
+ $index = 'map_seminarymedia_id';
+ break;
+ }
+
+ // Get media
+ $media = $this->Media->getSeminaryMediaById($seminary[$index]);
+
+ // Get file
+ $file = $this->getMediaFile($media, $action);
+ if(is_null($file)) {
+ return;
+ }
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: seminarymap
+ *
+ * Display the map of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of the Seminary
+ */
+ public function seminarymap($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get map
+ $map = $this->Map->getMapOfSeminary($seminary['id']);
+ if(is_null($map)) {
+ throw new \nre\exceptions\IdNotFoundException($seminaryUrl);
+ }
+
+ // Get media
+ $media = $this->Media->getSeminaryMediaById($map['seminarymedia_id']);
+
+ // Get file
+ $file = $this->getMediaFile($media);
+ if(is_null($file)) {
+ return;
+ }
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: seminary.
+ *
+ * Display a Seminary medium.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of the Seminary
+ * @param string $mediaUrl URL-name of the medium
+ * @param string $action Action for processing the media
+ */
+ public function seminary($seminaryUrl, $mediaUrl, $action=null)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Media
+ $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl);
+
+ // Get file
+ $file = $this->getMediaFile($media, $action);
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: avatar.
+ *
+ * Display an Avatar as full size or portrait.
+ *
+ * @throws \nre\exceptions\ParamsNotValidException
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of the Seminary
+ * @param string $charactertypeUrl URL-title of Character type
+ * @param int $xplevel XP-level
+ * @param string $action Size to show (avatar or portrait)
+ */
+ public function avatar($seminaryUrl, $charactertypeUrl, $xplevel, $action='avatar')
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Avatar
+ $avatar = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $charactertypeUrl, $xplevel);
+
+ // Get media
+ switch($action)
+ {
+ case null:
+ case 'avatar':
+ $media = $this->Media->getSeminaryMediaById($avatar['avatarpicture_id']);
+ $file = $this->getMediaFile($media, 'avatar');
+ break;
+ case 'portrait':
+ $media = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']);
+ $file = $this->getMediaFile($media);
+ break;
+ default:
+ throw new \nre\exceptions\ParamsNotValidException($action);
+ break;
+ }
+
+ // Get file
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: achievement
+ *
+ * Display the achievement of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of the Seminary
+ * @param string $achievementUrl URL-title of the Achievement
+ * @param string $action Action for processing the media
+ */
+ public function achievement($seminaryUrl, $achievementUrl, $locked=null)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character
+ $character = SeminaryController::$character;
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl);
+
+ // Get media
+ switch($locked)
+ {
+ case null:
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) == 0) {
+ if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ $index = 'achieved_achievementsmedia_id';
+ break;
+ case 'locked':
+ $index = 'unachieved_achievementsmedia_id';
+ break;
+ default:
+ throw new \nre\exceptions\ParamsNotValidException($locked);
+ break;
+ }
+ if(is_null($achievement[$index])) {
+ throw new \nre\exceptions\IdNotFoundException($achievementUrl);
+ }
+ $media = $this->Media->getSeminaryMediaById($achievement[$index]);
+
+ // Get file
+ $file = $this->getMediaFile($media, null);
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactergroup
+ *
+ * Display the icon for a Character group of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $groupUrl URL-Title of a Character group
+ */
+ public function charactergroup($seminaryUrl, $groupsgroupUrl, $groupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character group
+ $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl);
+
+ // Check media
+ if(is_null($group['charactergroupsmedia_id'])) {
+ $this->redirect($this->linker->link(array('grafics','charactergroup.jpg')));
+ }
+
+ // Get media
+ $media = $this->Media->getSeminaryMediaById($group['charactergroupsmedia_id']);
+
+ // Get file
+ $file = $this->getMediaFile($media, 'charactergroup');
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactergroupsquest
+ *
+ * Display the icon for a Character groups Quest of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $questUrl URL-Title of a Character groups Quest
+ */
+ public function charactergroupsquest($seminaryUrl, $groupsgroupUrl, $questUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character groups Quests
+ $quest = $this->Charactergroupsquests->getQuestByUrl($groupsgroup['id'], $questUrl);
+
+ // Check media
+ if(is_null($quest['questsmedia_id'])) {
+ $this->redirect($this->linker->link(array('grafics','charactergroup.jpg')));
+ }
+
+ // Get media
+ $media = $this->Media->getSeminaryMediaById($quest['questsmedia_id']);
+
+ // Get file
+ $file = $this->getMediaFile($media, 'charactergroupsquest');
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+
+
+ /**
+ * Determine file information and set the HTTP-header for
+ * caching accordingly.
+ *
+ * @param string $fileName Filename
+ * @return boolean HTTP-status 304 was set (in cache)
+ */
+ private function setCacheHeaders($fileName)
+ {
+ // Determine last change of file
+ $fileLastModified = gmdate('r', filemtime($fileName));
+
+ // Generate E-Tag
+ $fileEtag = hash('sha256', $fileLastModified.$fileName);
+
+
+ // Set header
+ $this->response->addHeader("Last-Modified: ".$fileLastModified);
+ $this->response->addHeader("Etag: ".$fileEtag);
+ // HTTP-status
+ $headerModifiedSince = $this->request->getServerParam('HTTP_IF_MODIFIED_SINCE');
+ $headerNoneMatch = $this->request->getServerParam('HTTP_IF_NONE_MATCH');
+ if(
+ !is_null($headerModifiedSince) && strtotime($fileLastModified) <= strtotime($headerModifiedSince) &&
+ !is_null($headerNoneMatch) && $headerNoneMatch == $fileEtag
+ ) {
+ $this->response->setExit(true);
+ $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304));
+
+ return true;
+ }
+
+
+ return false;
+ }
+
+
+ /**
+ * Determine the file for a medium and process it if necessary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @throws \nre\exceptions\ParamsNotValidException
+ * @param array $media Medium to get file for
+ * @param string $action Action for processing the media
+ * @return object File for the medium (or null if medium is cached)
+ */
+ private function getMediaFile($media, $action=null)
+ {
+ // Get format
+ $format = explode('/', $media['mimetype']);
+ $format = $format[1];
+
+ // Set content-type
+ $this->response->addHeader("Content-type: ".$media['mimetype']."");
+
+ // Set filename
+ $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id'];
+ if(!file_exists($media['filename'])) {
+ throw new \nre\exceptions\IdNotFoundException($media['id'].': '.$media['url']);
+ }
+
+ // Cache
+ if($this->setCacheHeaders($media['filename'])) {
+ return null;
+ }
+
+ // Load and process file
+ $file = null;
+ if(is_null($action) || !in_array(strtoupper($format), self::getImageTypes()))
+ {
+ // Do not process the file
+ $file = file_get_contents($media['filename']);
+ }
+ else
+ {
+ // Process file
+ switch($action)
+ {
+ case 'questgroup':
+ case 'quest':
+ case 'avatar':
+ case 'charactergroup':
+ case 'charactergroupsquest':
+ $file = self::resizeImage(
+ $media['filename'],
+ $format,
+ \nre\configs\AppConfig::$media[$action]['width'],
+ \nre\configs\AppConfig::$media[$action]['height']
+ );
+ break;
+ default:
+ throw new ParamsNotValidException($action);
+ break;
+ }
+ }
+
+
+ // Return file
+ return $file;
+ }
+
+
+ /**
+ * Get supported image types.
+ *
+ * @return array List of supported image types
+ */
+ private static function getImageTypes()
+ {
+ $im = new \Imagick();
+
+
+ return $im->queryFormats();
+ }
+
+
+ /**
+ * Resize an image.
+ *
+ * @param string $fileName Absolute pathname of image to resize
+ * @param string $mimeType Mimetype of target image
+ * @param int $width Max. width to resize to
+ * @param int $height Max. height to resize to
+ * @return mixed Resized image
+ */
+ private static function resizeImage($fileName, $mimeType, $width, $height)
+ {
+ // Read image from cache
+ $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($fileName).'-'.$width.'x'.$height;
+ if(file_exists($tempFileName))
+ {
+ // Check age of file
+ if(filemtime($fileName) > filemtime($tempFileName)) {
+ // Too old, delete
+ unlink($tempFileName);
+ }
+ else {
+ // Valid, read and return
+ return file_get_contents($tempFileName);
+ }
+ }
+
+
+ // ImageMagick
+ $im = new \Imagick($fileName);
+
+ // Calculate new size
+ $geometry = $im->getImageGeometry();
+ if($geometry['width'] < $width) {
+ $width = $geometry['width'];
+ }
+ if($geometry['height'] < $height) {
+ $height = $geometry['width'];
+ }
+
+ // Process
+ $im->thumbnailImage($width, $height, true);
+ $im->contrastImage(1);
+ $im->setImageFormat($mimeType);
+
+ // Save temporary file
+ $im->writeImage($tempFileName);
+
+
+ // Return resized image
+ return $im;
+ }
+
+ }
+
+?>
diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc
new file mode 100644
index 00000000..26854b16
--- /dev/null
+++ b/controllers/MenuController.inc
@@ -0,0 +1,64 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to display a menu.
+ *
+ * @author Oliver Hanraths
+ */
+ class MenuController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('pages');
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Set userdata
+ $this->set('loggedUser', IntermediateController::$user);
+ $this->set('loggedCharacter', SeminaryController::$character);
+ $this->set('loggedSeminary', SeminaryController::$seminary);
+ }
+
+
+ /**
+ * Action: index.
+ */
+ public function index()
+ {
+ // Get additional pages
+ $pages = $this->Pages->getPages();
+
+
+ // Pass data to view
+ $this->set('pages', $pages);
+ }
+
+ }
+
+?>
diff --git a/controllers/MoodpicController.inc b/controllers/MoodpicController.inc
new file mode 100644
index 00000000..2d88aca7
--- /dev/null
+++ b/controllers/MoodpicController.inc
@@ -0,0 +1,126 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the MoodpicAgent to display a Moodpic.
+ *
+ * @author Oliver Hanraths
+ */
+ class MoodpicController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroups', 'media');
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Show the moodpic of a Seminary Agent.
+ *
+ * @param string $agentName Name of the Seminary Agent to show moodpic of
+ */
+ public function index($agentName)
+ {
+ }
+
+
+ /**
+ * Action: seminary.
+ *
+ * Show moodpic of a Seminary.
+ *
+ * @param string $seminaryUrl URL of Seminary to show moodpic of
+ */
+ public function seminary($seminaryUrl, $agentName=null)
+ {
+ // Get Seminary
+ if(property_exists('\hhu\z\controllers\SeminaryController', 'seminary') && !empty(SeminaryController::$seminary) && SeminaryController::$seminary['url'] == $seminaryUrl) {
+ $seminary = SeminaryController::$seminary;
+ }
+ else {
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+ }
+
+ // Get medium
+ $medium = null;
+ // Agent medium
+ $seminaryMoodpicIndex = sprintf('%s_seminarymedia_id', $agentName);
+ if(array_key_exists($seminaryMoodpicIndex, $seminary) && !is_null($seminary[$seminaryMoodpicIndex]))
+ {
+ $medium = $this->Media->getSeminaryMediaById($seminary[$seminaryMoodpicIndex]);
+ $medium['url'] = $this->linker->link(array('media', 'seminarymoodpic', $seminary['url'], $agentName));
+ }
+ // Seminary medium
+ elseif(!is_null($seminary['seminarymedia_id']))
+ {
+ $medium = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']);
+ $medium['url'] = $this->linker->link(array('media', 'seminarymoodpic', $seminary['url']));
+ }
+
+
+ // Pass data to view
+ $this->set('medium', $medium);
+ }
+
+
+ /**
+ * Action: questgroup.
+ *
+ * Show moodpic of a Questgroup.
+ *
+ * @param string $seminaryUrl URL of Seminary
+ * @param string $quesgroupUrl URL of Questgroup to show moodpic of
+ */
+ public function questgroup($seminaryUrl, $questgroupUrl)
+ {
+ // Get Seminary
+ if(property_exists('\hhu\z\controllers\SeminaryController', 'seminary') && !empty(SeminaryController::$seminary) && SeminaryController::$seminary['url'] == $seminaryUrl) {
+ $seminary = SeminaryController::$seminary;
+ }
+ else {
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+ }
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+
+ // Get medium
+ $medium = null;
+ // Questgroup medium
+ if(!is_null($questgroup['questgroupspicture_id']))
+ {
+ $medium = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']);
+ $medium['url'] = $this->linker->link(array('media', 'seminary', $seminary['url'], $medium['url']));
+ }
+ // Seminary medium
+ elseif(!is_null($seminary['seminarymedia_id']))
+ {
+ $medium = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']);
+ $medium['url'] = $this->linker->link(array('media', 'seminarymoodpic', $seminary['url']));
+ }
+
+
+ // Pass data to view
+ $this->set('medium', $medium);
+ }
+
+ }
+
+?>
diff --git a/controllers/PagesController.inc b/controllers/PagesController.inc
new file mode 100644
index 00000000..eb6b2cdd
--- /dev/null
+++ b/controllers/PagesController.inc
@@ -0,0 +1,237 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the PagesAgent to manage additional pages.
+ *
+ * @author Oliver Hanraths
+ */
+ class PagesController extends \hhu\z\controllers\IntermediateController
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('pages');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin')
+ /*
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'delete' => array('admin')
+ */
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List all registered pages.
+ */
+ public function index()
+ {
+ // Get registered pages
+ $pages = $this->Pages->getPages();
+
+
+ // Pass data to view
+ $this->set('pages', $pages);
+ }
+
+
+ /**
+ * Action: page.
+ *
+ * Show a page.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $pageUrl URL of page to show
+ */
+ public function page($pageUrl)
+ {
+ // Get page
+ $page = $this->Pages->getPageByUrl($pageUrl);
+
+
+ // Pass data to view
+ $this->set('page', $page);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new page.
+ */
+ public function create()
+ {
+ // Values
+ $title = '';
+ $fields = array('title');
+ $validation = array();
+
+ // Create new page
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Pages->titleExists($title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+
+ // Create
+ if($validation === true)
+ {
+ $pageId = $this->Pages->createPage(
+ $this->Auth->getUserId(),
+ $title
+ );
+
+ // Redirect to page
+ $page = $this->Pages->getPageById($pageId);
+ $this->redirect($this->linker->link(array('page', $page['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Pass data to view
+ $this->set('title', $title);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit page.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $pageUrl URL of page to edit
+ */
+ public function edit($pageUrl)
+ {
+ // Get page
+ $page = $this->Pages->getPageByUrl($pageUrl);
+
+ // Values
+ $title = $page['title'];
+ $text = $page['text'];
+ $fields = array('title');
+ $validation = array();
+
+ // Edit content
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('save')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Pages->titleExists($title, $page['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $text = $this->request->getPostParam('text');
+ var_dump($text);
+
+ // Edit
+ if($validation === true)
+ {
+ $this->Pages->editPage(
+ $page['id'],
+ $title,
+ $text
+ );
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('page', $page['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Pass data to view
+ $this->set('page', $page);
+ $this->set('title', $title);
+ $this->set('text', $text);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a page.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $pageUrl URL of page to delete
+ */
+ public function delete($pageUrl)
+ {
+ // Get page
+ $page = $this->Pages->getPageByUrl($pageUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete page
+ $this->Pages->deletePage($page);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(null, 1));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('page', $page['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete page');
+
+ // Show confirmation
+ $this->set('page', $page);
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc
new file mode 100644
index 00000000..a696ba61
--- /dev/null
+++ b/controllers/QuestgroupsController.inc
@@ -0,0 +1,729 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the QuestgroupsAgent to display Questgroups.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroupshierarchy', 'questgroups', 'questgrouptexts', 'quests', 'questtexts', 'media');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'questgroup' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'edittexts' => array('admin', 'moderator', 'user'),
+ 'moveup' => array('admin', 'moderator', 'user'),
+ 'movedown' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'questgroup' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin'),
+ 'edit' => array('admin', 'moderator'),
+ 'edittexts' => array('admin', 'moderator'),
+ 'moveup' => array('admin'),
+ 'movedown' => array('admin'),
+ 'delete' => array('admin')
+ );
+
+
+
+
+ /**
+ * Action: questgroup.
+ *
+ * Display a Questgroup and its data.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $questgroupUrl URL-Title of a Questgroup
+ */
+ public function questgroup($seminaryUrl, $questgroupUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+
+ // Get Questgrouphierarchy
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+
+ // Get Character
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+
+ // Check permission
+ if(count(array_intersect(array('admin','moderator'), SeminaryController::$character['characterroles'])) == 0)
+ {
+ // Check permission of previous parent Questgroups
+ $parentQuestgroup = $questgroup;
+ while(!is_null($parentQuestgroup['hierarchy']['parent_questgroup_id']))
+ {
+ $parentQuestgroup = $this->Questgroups->getQuestgroupById($parentQuestgroup['hierarchy']['parent_questgroup_id']);
+ $parentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($parentQuestgroup['id']);
+ try {
+ $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($seminary['id'], $parentQuestgroup['id']);
+ if(!$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ }
+ }
+
+ // Check permission of previous Questgroup
+ if($this->Questgroups->hasCharacterEnteredQuestgroup($questgroup['id'], $character['id']))
+ {
+ $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($seminary['id'], $questgroup['id']);
+ if(!is_null($previousQuestgroup)) {
+ if(!$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ }
+ }
+
+ // Set status “entered”
+ $this->Questgroups->setQuestgroupEntered($questgroup['id'], $character['id']);
+
+ // Get child Questgroupshierarchy
+ $childQuestgroupshierarchy = null;
+ if(!empty($questgroup['hierarchy']))
+ {
+ $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']);
+ foreach($childQuestgroupshierarchy as &$hierarchy)
+ {
+ // Get Questgroups
+ $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']);
+
+ // Get additional data
+ foreach($hierarchy['questgroups'] as $i => &$group)
+ {
+ $group['solved'] = $this->Questgroups->hasCharacterSolvedQuestgroup($group['id'], $character['id']);
+
+ // Check permission of Questgroups
+ if($i >= 1 && count(array_intersect(array('admin','moderator'), SeminaryController::$character['characterroles'])) == 0)
+ {
+ if(!$hierarchy['questgroups'][$i-1]['solved'])
+ {
+ $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i);
+ break;
+ }
+ }
+
+ // Get Character XPs
+ $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']);
+
+ // Attach related Questgroups
+ $group['relatedQuestgroups'] = array();
+ $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuestgroup($group['id']);
+ foreach($relatedQuestgroups as &$relatedQuestgroup) {
+ if($this->Questgroups->hasCharacterEnteredQuestgroup($relatedQuestgroup['id'], $character['id'])) {
+ $group['relatedQuestgroups'][] = $this->Questgroups->getQuestgroupById($relatedQuestgroup['id']);
+ }
+
+ }
+ }
+ }
+ }
+
+ // Get texts
+ $questgroupTexts = $this->Questgrouptexts->getQuestgroupTexts($questgroup['id']);
+
+ // Get Quests
+ $quests = array();
+ if(count($childQuestgroupshierarchy) == 0)
+ {
+ $currentQuest = null;
+ do {
+ // Get next Quest
+ if(is_null($currentQuest)) {
+ $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']);
+ }
+ else {
+ $nextQuests = $this->Quests->getNextQuests($currentQuest['id']);
+ $currentQuest = null;
+ foreach($nextQuests as &$nextQuest) {
+ if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) {
+ $currentQuest = $nextQuest;
+ break;
+ }
+ }
+ }
+
+ // Add additional data
+ if(!is_null($currentQuest))
+ {
+ // Set status
+ $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']);
+
+ // Attach related Questgroups
+ $currentQuest['relatedQuestgroups'] = array();
+ $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']);
+ foreach($relatedQuestgroups as &$relatedQuestgroup)
+ {
+ if($this->Questgroups->hasCharacterEnteredQuestgroup($relatedQuestgroup['id'], $character['id'])) {
+ $currentQuest['relatedQuestgroups'][] = $this->Questgroups->getQuestgroupById($relatedQuestgroup['id']);
+ }
+ }
+
+ // Add Quest to Quests
+ $quests[] = $currentQuest;
+ }
+ }
+ while(!is_null($currentQuest) && ($currentQuest['solved'] || count(array_intersect(array('admin','moderator'), SeminaryController::$character['characterroles'])) > 0));
+ }
+
+
+ // Set titile
+ if(!is_null($questgroup['hierarchy'])) {
+ $this->addTitle(sprintf('%s %d: %s', $questgroup['hierarchy']['title_singular'], $questgroup['hierarchy']['questgroup_pos'], $questgroup['title']));
+ }
+ else {
+ $this->addTitle($questgroup['title']);
+ }
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy);
+ $this->set('texts', $questgroupTexts);
+ $this->set('quests', $quests);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Questgroup.
+ *
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function create($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $selectedQuestgroupshierarchy = null;
+ $selectedQuestgroup = null;
+ $title = '';
+ $fields = array('title');
+ $validation = array();
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Questgroups->questgroupTitleExists($seminary['id'], $title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+
+ // Validate Questgroupshierarchy
+ try {
+ $selectedQuestgroupshierarchy = $this->Questgroupshierarchy->getHierarchyByUrl($seminary['id'], $this->request->getPostParam('questgroupshierarchy'));
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ throw new \nre\exceptions\ParamsNotValidException($this->request->getPostParam('questgroupshierarchy'));
+ }
+
+ // Validate Questgroup
+ if(!is_null($selectedQuestgroupshierarchy['parent_questgroupshierarchy_id'])) {
+ try {
+ $selectedQuestgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $this->request->getPostParam('questgroup'));
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ throw new \nre\exceptions\ParamsNotValidException($this->request->getPostParam('questgroups'));
+ }
+ }
+
+ // Validate moodpic
+ $moodpic = null;
+ if(!empty($_FILES) && array_key_exists('moodpic', $_FILES) && $_FILES['moodpic']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $moodpic = $_FILES['moodpic'];
+
+ // Check error
+ if($moodpic['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'error', $moodpic['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $moodpic['mimetype'] = \hhu\z\Utils::getMimetype($moodpic['tmp_name'], $moodpic['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $moodpic['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'mimetype', $moodpic['mimetype']);
+ }
+ elseif($moodpic['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Create new Questgroup
+ if($validation === true)
+ {
+ $questgroupId = $this->Questgroups->createQuestgroup(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $title
+ );
+ $questgroup = $this->Questgroups->getQuestgroupById($questgroupId);
+
+ // Add to Hierarchy
+ $this->Questgroups->addQuestgroupToHierarchy(
+ $questgroupId,
+ $selectedQuestgroupshierarchy['id'],
+ (!is_null($selectedQuestgroup)) ? $selectedQuestgroup['id'] : null
+ );
+
+ // Upload moodpic
+ if(!is_null($moodpic))
+ {
+ $mediaId = $this->Media->createQuestgrouppicture(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $questgroup['id'],
+ sprintf('questgroupmoodpic-%s', $questgroup['url']),
+ '',
+ $moodpic['mimetype'],
+ $moodpic['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Questgroups->setMoodpicForQuestgroup($questgroup['id'], $mediaId);
+ }
+ }
+
+
+ // Redirect to new Questgroup
+ $this->redirect($this->linker->link(array('questgroup', $seminary['url'], $questgroup['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Create Questgroup');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('questgroupshierarchy', $selectedQuestgroupshierarchy);
+ $this->set('questgroup', $selectedQuestgroup);
+ $this->set('title', $title);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a Questgroup.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of a Seminary
+ * @param string $questgroupUrl URL-title of Questgroup to edit
+ */
+ public function edit($seminaryUrl, $questgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $title = $questgroup['title'];
+ $fields = array('title');
+ $validation = array();
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Questgroups->questgroupTitleExists($seminary['id'], $title, $questgroup['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+
+ // Validate moodpic
+ $moodpic = null;
+ if(!empty($_FILES) && array_key_exists('moodpic', $_FILES) && $_FILES['moodpic']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $moodpic = $_FILES['moodpic'];
+
+ // Check error
+ if($moodpic['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'error', $moodpic['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $moodpic['mimetype'] = \hhu\z\Utils::getMimetype($moodpic['tmp_name'], $moodpic['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $moodpic['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'mimetype', $moodpic['mimetype']);
+ }
+ elseif($moodpic['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Edit Questgroup
+ if($validation === true)
+ {
+ $this->Questgroups->editQuestgroup(
+ $questgroup['id'],
+ $title
+ );
+ $questgroup = $this->Questgroups->getQuestgroupById($questgroup['id']);
+
+ // Upload moodpic
+ if(!is_null($moodpic))
+ {
+ $mediaId = $this->Media->createQuestgrouppicture(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $questgroup['id'],
+ sprintf('questgroupmoodpic-%s', $questgroup['url']),
+ '',
+ $moodpic['mimetype'],
+ $moodpic['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Questgroups->setMoodpicForQuestgroup($questgroup['id'], $mediaId);
+ }
+ }
+
+
+ // Redirect to new Questgroup
+ $this->redirect($this->linker->link(array('questgroup', $seminary['url'], $questgroup['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Questgroup');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('title', $title);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edittexts.
+ *
+ * Edit the texts of a Questgroup.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of a Seminary
+ * @param string $questgroupUrl URL-title of Questgroup to edit
+ */
+ public function edittexts($seminaryUrl, $questgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+
+ // Get Questgroup texts
+ $questgroupTexts = $this->Questgrouptexts->getQuestgroupTexts($questgroup['id']);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ $texts = $this->request->getPostParam('questgrouptexts');
+ $deleteTexts = $this->request->getPostParam('deletes');
+ if(!is_array($deleteTexts)) {
+ $deleteTexts = array();
+ }
+
+ // Edit or delete texts
+ foreach($questgroupTexts as $text)
+ {
+ if(array_key_exists($text['id'], $deleteTexts))
+ {
+ $this->Questgrouptexts->deleteQuestgrouptext($text);
+ unset($texts[$text['id']]);
+ }
+ elseif(array_key_exists($text['id'], $texts))
+ {
+ $this->Questgrouptexts->editQuestgrouptext($text['id'], $texts[$text['id']]);
+ unset($texts[$text['id']]);
+ }
+ }
+
+ // Add new texts
+ foreach($texts as $text) {
+ if(!empty($text)) {
+ $this->Questgrouptexts->addQuestgrouptextToQuestgroup($this->Auth->getUserId(), $questgroup['id'], $text);
+ }
+ }
+
+ // Redirect to Questgroup
+ $this->redirect($this->linker->link(array('questgroup', $seminary['url'], $questgroup['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Questgroup texts');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('questgrouptexts', $questgroupTexts);
+ $this->set('mimetypes', $mimetypes);
+ }
+
+
+ /**
+ * Action: moveup.
+ *
+ * Move a Questgroup up (decrement position).
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $questgroupUrl URL-title of Questgroup
+ */
+ public function moveup($seminaryUrl, $questgroupUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('move')))
+ {
+ // Set position
+ $this->Questgroups->moveQuestgroup($questgroup, true);
+ }
+
+ // Redirect
+ $referer = $this->request->getGetParam('referer');
+ if(!is_null($referer))
+ {
+ try {
+ $questgroup = $this->Questgroups->getQuestgroupById($referer);
+ $this->redirect($this->linker->link(array('questgroups', 'questgroup', $seminary['url'], $questgroup['url'])));
+ }
+ catch(IdNotFoundException $e) {
+ }
+ }
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Move Questgroup');
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ }
+
+
+ /**
+ * Action: movedown
+ *
+ * Move a Questgroup down (increment position).
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $questgroupUrl URL-title of Questgroup
+ */
+ public function movedown($seminaryUrl, $questgroupUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('move')))
+ {
+ // Set position
+ $this->Questgroups->moveQuestgroup($questgroup, false);
+ }
+
+ // Redirect
+ $referer = $this->request->getGetParam('referer');
+ if(!is_null($referer))
+ {
+ try {
+ $questgroup = $this->Questgroups->getQuestgroupById($referer);
+ $this->redirect($this->linker->link(array('questgroups', 'questgroup', $seminary['url'], $questgroup['url'])));
+ }
+ catch(IdNotFoundException $e) {
+ }
+ }
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Move Questgroup');
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Questgroup.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $questgroupUrl URL-Title of a Questgroup
+ */
+ public function delete($seminaryUrl, $questgroupUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+
+ // Get Questgrouphierarchy
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete Questgroup
+ $this->Questgroups->deleteQuestgroup($questgroup['id']);
+
+ // Redirect
+ if(!is_null($questgroup['hierarchy']))
+ {
+ // Parent Questgroup
+ if(is_null($questgroup['hierarchy']['parent_questgroup_id'])) {
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+ else
+ {
+ $parentQuestgroup = $this->Questgroups->getQuestgroupById($questgroup['hierarchy']['parent_questgroup_id']);
+ $this->redirect($this->linker->link(array('questgroup', $seminary['url'], $parentQuestgroup['url']), 1));
+ }
+ }
+ else
+ {
+ // Related Questgroup
+ $questtexts = $this->Questtexts->getRelatedQuesttextsForQuestgroup($questgroup['id']);
+ $questtext = $this->Questtexts->pickQuesttextLastEnteredByCharacter(\hhu\z\controllers\SeminaryController::$character['id'], $questtexts);
+ $quest = $this->Quests->getQuestById($questtext['quest_id']);
+ $relatedQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']);
+ $this->redirect($this->linker->link(array('questgroup', $seminary['url'], $relatedQuestgroup['url']), 1));
+ }
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('questgroup', $seminary['url'], $questgroup['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete Questgroup');
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestgroupshierarchyController.inc b/controllers/QuestgroupshierarchyController.inc
new file mode 100644
index 00000000..db3cb52c
--- /dev/null
+++ b/controllers/QuestgroupshierarchyController.inc
@@ -0,0 +1,346 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the QuestgroupsAgent to display Questgroups.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupshierarchyController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroupshierarchy', 'questgroups');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'create' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'moveup' => array('admin', 'moderator', 'user'),
+ 'movedown' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'create' => array('admin'),
+ 'edit' => array('admin'),
+ 'moveup' => array('admin'),
+ 'movedown' => array('admin'),
+ 'delete' => array('admin')
+ );
+
+
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Questgroupshierarchy.
+ *
+ * @param string $seminaryUrl URL-title of a Seminary
+ */
+ public function create($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get parent Questgroupshierarchy
+ $parentQuestgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminary['id']);
+
+ // Values
+ $titleSingular = '';
+ $titlePlural = '';
+ $validation = array();
+
+ // Create Questgroupshierarchy
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $titleSingular = $this->request->getPostParam('title_singular');
+ $validation = $this->Validation->addValidationResults(
+ $validation,
+ 'titleSingular',
+ $this->Validation->validate($titleSingular, \nre\configs\AppConfig::$validation['questgroupshierarchytitle'])
+ );
+ if($this->Questgroupshierarchy->questgroupshierarchyTitleSingularExists($titleSingular, $seminary['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'titleSingular', 'exist', true);
+ }
+ $titlePlural = $this->request->getPostParam('title_plural');
+ $validation = $this->Validation->addValidationResults(
+ $validation,
+ 'titlePlural',
+ $this->Validation->validate($titlePlural, \nre\configs\AppConfig::$validation['questgroupshierarchytitle'])
+ );
+ if($this->Questgroupshierarchy->questgroupshierarchyTitleSingularExists($titlePlural, $seminary['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'titlePlural', 'exist', true);
+ }
+
+ // Validate parent Questgroupshierarchy
+ $parentQuestgroupshierarchyIndex = null;
+ foreach($parentQuestgroupshierarchy as $index => &$parentHierarchy)
+ {
+ $parentHierarchy['selected'] = ($parentHierarchy['url'] == $this->request->getPostParam('parent'));
+ if($parentHierarchy['selected']) {
+ $parentQuestgroupshierarchyIndex = $index;
+ }
+ }
+
+ // Create new Questgroupshierarchy
+ if($validation === true)
+ {
+ $questgroupshierarchyId = $this->Questgroupshierarchy->createQuestgroupshierarchy(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ (!is_null($parentQuestgroupshierarchyIndex) ? $parentQuestgroupshierarchy[$parentQuestgroupshierarchyIndex]['id'] : null),
+ $titleSingular,
+ $titlePlural
+ );
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyById($questgroupshierarchyId);
+
+ // Redirect
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array(
+ 'title' => \nre\configs\AppConfig::$validation['title']
+ );
+
+
+ // Set titile
+ $this->addTitleLocalized('Create Questgroupshierarchy');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('titleSingular', $titleSingular);
+ $this->set('titlePlural', $titlePlural);
+ $this->set('parentQuestgroupshierarchy', $parentQuestgroupshierarchy);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a Questgroupshierarchy.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $questgroupshierarchyUrl URL-title of Questgroupshierarchy
+ */
+ public function edit($seminaryUrl, $questgroupshierarchyUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroupshierarchy
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyByUrl($seminary['id'], $questgroupshierarchyUrl);
+
+ // Values
+ $titleSingular = $questgroupshierarchy['title_singular'];
+ $titlePlural = $questgroupshierarchy['title_plural'];
+ $validation = array();
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $titleSingular = $this->request->getPostParam('title_singular');
+ $validation = $this->Validation->addValidationResults(
+ $validation,
+ 'titleSingular',
+ $this->Validation->validate($titleSingular, \nre\configs\AppConfig::$validation['questgroupshierarchytitle'])
+ );
+ if($this->Questgroupshierarchy->questgroupshierarchyTitleSingularExists($titleSingular, $seminary['id'], $questgroupshierarchy['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'titleSingular', 'exist', true);
+ }
+ $titlePlural = $this->request->getPostParam('title_plural');
+ $validation = $this->Validation->addValidationResults(
+ $validation,
+ 'titlePlural',
+ $this->Validation->validate($titlePlural, \nre\configs\AppConfig::$validation['questgroupshierarchytitle'])
+ );
+ if($this->Questgroupshierarchy->questgroupshierarchyTitleSingularExists($titlePlural, $seminary['id'], $questgroupshierarchy['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'titlePlural', 'exist', true);
+ }
+
+
+ // Edit Questgroup
+ if($validation === true)
+ {
+ $this->Questgroupshierarchy->editQuestgroupshierarchy(
+ $questgroupshierarchy['id'],
+ $titleSingular,
+ $titlePlural
+ );
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyById($questgroupshierarchy['id']);
+
+ // Redirect to Seminary page
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array(
+ 'title' => \nre\configs\AppConfig::$validation['title']
+ );
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Questgroupshierarchy');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('titleSingular', $titleSingular);
+ $this->set('titlePlural', $titlePlural);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: moveup.
+ *
+ * Move a Questgroupshierarchy up (decrement position).
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $questgroupshierarchyUrl URL-title of Questgroupshierarchy
+ */
+ public function moveup($seminaryUrl, $questgroupshierarchyUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroupshierarchy
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyByUrl($seminary['id'], $questgroupshierarchyUrl);
+
+ // Set position
+ $this->Questgroupshierarchy->moveQuestgroupshierarchy($questgroupshierarchy, true);
+
+
+ // Redirect
+ $referer = $this->request->getGetParam('referer');
+ if(!is_null($referer))
+ {
+ try {
+ $questgroup = $this->Questgroups->getQuestgroupById($referer);
+ $this->redirect($this->linker->link(array('questgroups', 'questgroup', $seminary['url'], $questgroup['url'])));
+ }
+ catch(IdNotFoundException $e) {
+ }
+ }
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+
+
+ /**
+ * Action: movedown.
+ *
+ * Move a Questgroupshierarchy down (increment position).
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $questgroupshierarchyUrl URL-title of Questgroupshierarchy
+ */
+ public function movedown($seminaryUrl, $questgroupshierarchyUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroupshierarchy
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyByUrl($seminary['id'], $questgroupshierarchyUrl);
+
+ // Set position
+ $this->Questgroupshierarchy->moveQuestgroupshierarchy($questgroupshierarchy, false);
+
+
+ // Redirect
+ $referer = $this->request->getGetParam('referer');
+ if(!is_null($referer))
+ {
+ try {
+ $questgroup = $this->Questgroups->getQuestgroupById($referer);
+ $this->redirect($this->linker->link(array('questgroups', 'questgroup', $seminary['url'], $questgroup['url'])));
+ }
+ catch(IdNotFoundException $e) {
+ }
+ }
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Questgroupshierarchy.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $questgroupshierarchyUrl URL-title of Questgroupshierarchy
+ */
+ public function delete($seminaryUrl, $questgroupshierarchyUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroupshierarchy
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyByUrl($seminary['id'], $questgroupshierarchyUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete Questgroupshierarchy
+ $this->Questgroupshierarchy->deleteQuestgroupshierarchy($questgroupshierarchy['id']);
+ }
+
+ // Redirect to Seminary page
+ $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url'])));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete Questgroupshierarchy');
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ $this->set('questgroupshierarchy', $questgroupshierarchy);
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc
new file mode 100644
index 00000000..3ced4e25
--- /dev/null
+++ b/controllers/QuestgroupshierarchypathController.inc
@@ -0,0 +1,91 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of QuestgroupshierarchypathAgent to display the
+ * Questgroups hierarchy path.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupshierarchypathController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts');
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Calculate and show the hierarchy path of a Questgroup.
+ *
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $questgroupUrl URL-Title of a Questgroup
+ * @param boolean $showGroup Show the current group itself
+ */
+ public function index($seminaryUrl, $questgroupUrl, $showGroup=false)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+
+ // Get parent Questgrouphierarchy
+ $currentQuestgroup = $questgroup;
+ $parentQuestgroupshierarchy = array();
+ if($showGroup) {
+ array_unshift($parentQuestgroupshierarchy, $currentQuestgroup);
+ }
+ if(is_null($questgroup['hierarchy']))
+ {
+ // Get related Questgroup
+ $questtexts = $this->Questtexts->getRelatedQuesttextsForQuestgroup($currentQuestgroup['id']);
+ $questtext = $this->Questtexts->pickQuesttextLastEnteredByCharacter(\hhu\z\controllers\SeminaryController::$character['id'], $questtexts);
+ $quest = $this->Quests->getQuestById($questtext['quest_id']);
+ $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']);
+ $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']);
+ $quest['questgroup'] = $currentQuestgroup;
+
+ // Use Hierarchy name for optional Questgroup
+ if(!empty($parentQuestgroupshierarchy)) {
+ $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy'];
+ unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']);
+ }
+
+ array_unshift($parentQuestgroupshierarchy, $quest);
+ array_unshift($parentQuestgroupshierarchy, $currentQuestgroup);
+ }
+ while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id']))
+ {
+ $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']);
+ $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']);
+ array_unshift($parentQuestgroupshierarchy, $currentQuestgroup);
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy);
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc
new file mode 100644
index 00000000..931621b9
--- /dev/null
+++ b/controllers/QuestsController.inc
@@ -0,0 +1,1504 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the QuestsAgent to display Quests.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroups', 'quests', 'questtexts', 'media', 'questtypes', 'questgroupshierarchy', 'xplevels');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'quest' => array('admin', 'moderator', 'user'),
+ 'submissions' => array('admin', 'moderator', 'user'),
+ 'submission' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'edittexts' => array('admin', 'moderator', 'user'),
+ 'edittask' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator'),
+ 'quest' => array('admin', 'moderator', 'user'),
+ 'submissions' => array('admin', 'moderator'),
+ 'submission' => array('admin', 'moderator'),
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'edittexts' => array('admin', 'moderator'),
+ 'edittask' => array('admin', 'moderator'),
+ 'delete' => array('admin')
+ );
+
+
+
+
+ /**
+ * Prefilter that is executed before running the Controller.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ $this->checkAchievements($request, array('character', 'quest'));
+ $this->checkAchievements($request, 'achievement');
+ }
+
+
+ /**
+ * Action: index.
+ *
+ * List all Quests for a Seminary.
+ *
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $all Show all Quests
+ */
+ public function index($seminaryUrl, $all=null)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Set filters
+ $selectedQuestgroup = null;
+ $selectedQuesttype = null;
+ $selectedTitle = null;
+ $page = 1;
+ if($this->request->getRequestMethod() == 'GET')
+ {
+ $selectedQuestgroup = $this->request->getGetParam('questgroup');
+ $selectedQuestgroup = !empty($selectedQuestgroup) ? $selectedQuestgroup : null;
+ $selectedQuesttype = $this->request->getGetParam('questtype');
+ $selectedQuesttype = !empty($selectedQuesttype) ? $selectedQuesttype : null;
+ $selectedTitle = $this->request->getGetParam('title');
+ $selectedTitle = !empty($selectedTitle) ? $selectedTitle : null;
+ $page = $this->request->getGetParam('page');
+ $page = !empty($page) ? intval($page) : 1;
+ }
+
+ // Get Quests
+ $limit = ($all != 'all') ? \nre\configs\AppConfig::$misc['lists_limit'] : null;
+ $offset = ($all != 'all') ? max((intval($page) - 1), 0) * $limit : 0;
+ $questsCount = $this->Quests->getCountForQuestsForSeminaryByOpenSubmissions($seminary['id'], $selectedQuestgroup, $selectedQuesttype, $selectedTitle);
+ $quests = $this->Quests->getQuestsForSeminaryByOpenSubmissions($seminary['id'], $selectedQuestgroup, $selectedQuesttype, $selectedTitle, $limit, $offset);
+ foreach($quests as &$quest)
+ {
+ // Get Questtype
+ $quest['questtype'] = $this->Questtypes->getQuesttypeById($quest['questtype_id']);
+ }
+
+ // Get filter values
+ $questgroups = $this->Questgroups->getQuestgroupsForSeminary($seminary['id']);
+ $questtypes = $this->Questtypes->getQuesttypes();
+
+
+ // Set titile
+ $this->addTitleLocalized('Quests');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroups', $questgroups);
+ $this->set('questtypes', $questtypes);
+ $this->set('selectedQuestgroup', $selectedQuestgroup);
+ $this->set('selectedQuesttype', $selectedQuesttype);
+ $this->set('selectedTitle', $selectedTitle);
+ $this->set('quests', $quests);
+ $this->set('questsCount', $questsCount);
+ $this->set('all', $all);
+ $this->set('page', $page);
+ $this->set('limit', $limit);
+ }
+
+
+ /**
+ * Action: quest.
+ *
+ * Show a quest and its task.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ * @param string $questUrl URL-Title of Quest
+ * @param string $questtexttypeUrl URL-Title of Questtexttype
+ * @param int $questtextPos Position of Questtext
+ */
+ public function quest($seminaryUrl, $questgroupUrl, $questUrl, $questtexttypeUrl=null, $questtextPos=1)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['picture'] = null;
+ if(!is_null($questgroup['questgroupspicture_id'])) {
+ $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']);
+ }
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Get Character
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+
+ // Check permissions
+ if(count(array_intersect(array('admin','moderator'), SeminaryController::$character['characterroles'])) == 0)
+ {
+ // Previous Questgroup
+ $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($seminary['id'], $questgroup['id']);
+ if(!is_null($previousQuestgroup) && !$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ // Previous parents of previous Questgroup
+ $parentQuestgroup = $previousQuestgroup;
+ $parentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($parentQuestgroup['id']);
+ while(!is_null($parentQuestgroup['hierarchy']['parent_questgroup_id']))
+ {
+ $parentQuestgroup = $this->Questgroups->getQuestgroupById($parentQuestgroup['hierarchy']['parent_questgroup_id']);
+ $parentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($parentQuestgroup['id']);
+ try {
+ $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($seminary['id'], $parentQuestgroup['id']);
+ if(!$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ }
+ }
+
+ // Previous Quests
+ $previousQuests = $this->Quests->getPreviousQuests($quest['id']);
+ if(count($previousQuests) > 0)
+ {
+ // Previous Quests
+ // One previous Quest has to be solved and no other
+ // following Quest of ones has to be entered
+ // except it is also an previous Quest of the current Quest
+ $solved = false;
+ $tried = false;
+ foreach($previousQuests as &$previousQuest)
+ {
+ // Check previous Quest
+ if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id']))
+ {
+ $solved = true;
+
+ // Check following Quests
+ $followingQuests = $this->Quests->getNextQuests($previousQuest['id']);
+ foreach($followingQuests as $followingQuest)
+ {
+ // Check following Quest
+ if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterEnteredQuest($followingQuest['id'], $character['id']))
+ {
+ $followingFollowingQuests = $this->Quests->getNextQuests($followingQuest['id']);
+ if(!in_array($quest['id'], array_map(function($q) { return $q['id']; }, $followingFollowingQuests)))
+ {
+ $tried = true;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ if(!$solved || $tried) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ }
+
+ // Set status “entered”
+ $this->Quests->setQuestEntered($quest['id'], $character['id']);
+
+ // Has Character solved quest?
+ $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+ // Get Questtexts
+ $questtexts = array();
+ $questtexts['Prolog'] = $this->Questtexts->getQuesttextsOfQuest($quest['id'], 'Prolog');
+ if($solved || count(array_intersect(array('admin','moderator'), SeminaryController::$character['characterroles'])) > 0) {
+ $questtexts['Epilog'] = $this->Questtexts->getQuesttextsOfQuest($quest['id'], 'Epilog');
+ }
+ foreach($questtexts as &$questtextList)
+ {
+ foreach($questtextList as &$questtext)
+ {
+ // Questtext media
+ if(!is_null($questtext['questsmedia_id'])) {
+ $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']);
+ }
+
+ // Related Questgroups
+ $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']);
+ }
+ }
+
+ // Quest status
+ $questStatus = $this->request->getGetParam('status');
+
+ // Quest media
+ $questmedia = null;
+ if(!is_null($quest['questsmedia_id'])) {
+ $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']);
+ }
+
+ // Task
+ $task = null;
+ $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']);
+ if(!is_null($questtype['classname'])) {
+ $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character);
+ }
+ else
+ {
+ // Mark Quest as solved
+ $this->Quests->setQuestSolved($quest['id'], $character['id']);
+ $solved = true;
+ }
+
+ // Get (related) Questtexts
+ $relatedQuesttexts = $this->Questtexts->getRelatedQuesttextsForQuestgroup($questgroup['id']);
+ $relatedQuesttext = $this->Questtexts->pickQuesttextLastEnteredByCharacter($character['id'], $relatedQuesttexts);
+ if(!is_null($relatedQuesttext)) {
+ $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']);
+ if(!empty($relatedQuesttext['quest'])) {
+ $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url'];
+ }
+ }
+
+ // Next Quest/Questgroup
+ $nextQuests = null;
+ $charactedHasChoosenNextQuest = false;
+ $nextQuestgroup = null;
+ if($solved || count(array_intersect(array('admin','moderator'), SeminaryController::$character['characterroles'])) > 0)
+ {
+ // Next Quest
+ $nextQuests = $this->Quests->getNextQuests($quest['id']);
+ foreach($nextQuests as &$nextQuest)
+ {
+ // Set entered status of Quest
+ $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']);
+ if($nextQuest['entered']) {
+ $charactedHasChoosenNextQuest = true;
+ }
+ }
+
+ // Next Questgroup
+ if(empty($nextQuests))
+ {
+ if(is_null($relatedQuesttext))
+ {
+ $nextQuestgroup = $this->Questgroups->getNextQuestgroup($seminary['id'], $questgroup['id']);
+ if(!is_null($nextQuestgroup)) {
+ $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']);
+ }
+ }
+ else
+ {
+ // Related (Main-) Quest
+ $nextQuest = $relatedQuesttext['quest'];
+ $nextQuest['entered'] = true;
+ $nextQuests = array($nextQuest);
+ }
+ }
+ }
+
+
+ // Set titile
+ $this->addTitle($quest['title']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('questtexts', $questtexts);
+ $this->set('quest', $quest);
+ $this->set('queststatus', $questStatus);
+ $this->set('relatedquesttext', $relatedQuesttext);
+ $this->set('nextquests', $nextQuests);
+ $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest);
+ $this->set('nextquestgroup', $nextQuestgroup);
+ $this->set('task', $task);
+ $this->set('media', $questmedia);
+ $this->set('solved', $solved);
+ }
+
+
+ /**
+ * List Character submissions for a Quest.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ * @param string $questUrl URL-Title of Quest
+ */
+ public function submissions($seminaryUrl, $questgroupUrl, $questUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['picture'] = null;
+ if(!is_null($questgroup['questgroupspicture_id'])) {
+ $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']);
+ }
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Get submitted Character submissions waiting for approval
+ $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']);
+
+ // Get unsolved Character submissions
+ $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']);
+
+ // Get solved Character submissions
+ $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']);
+
+
+ // Set titile
+ $this->addTitleLocalized('Submissions');
+ $this->addTitle($quest['title']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters);
+ $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters);
+ $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters);
+ }
+
+
+ /**
+ * Show and handle the submission of a Character for a Quest.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ * @param string $questUrl URL-Title of Quest
+ * @param string $characterUrl URL-Title of Character
+ */
+ public function submission($seminaryUrl, $questgroupUrl, $questUrl, $characterUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['picture'] = null;
+ if(!is_null($questgroup['questgroupspicture_id'])) {
+ $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']);
+ }
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Character
+ $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl);
+
+ // Questtype
+ $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']);
+
+ // Render Questtype output
+ $output = $this->renderTaskSubmission($questtype['classname'], $seminary, $questgroup, $quest, $character);
+
+
+ // Set titile
+ $this->addTitleLocalized('Submission of %s', $character['name']);
+ $this->addTitle($quest['title']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('character', $character);
+ $this->set('output', $output);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Quest.
+ *
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ */
+ public function create($seminaryUrl, $questgroupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+ $questgroup['picture'] = (!is_null($questgroup['questgroupspicture_id'])) ? $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']) : null;
+
+ // Get Quest types
+ $questtypes = $this->Questtypes->getQuesttypes();
+ foreach($questtypes as &$questtype) {
+ $questtype['selected'] = false;
+ }
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $title = '';
+ $xps = 0;
+ $task = '';
+ $entryText = '';
+ $wrongText = '';
+ $fields = array('title', 'xps');
+ $validation = array();
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Quests->questTitleExists($title, $seminary['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $xps = $this->request->getPostParam('xps');
+ $task = $this->request->getPostParam('task');
+ $entryText = $this->request->getPostParam('entrytext');
+ $wrongText = $this->request->getPostParam('wrongtext');
+
+ // Validate Questtype
+ $questtypeIndex = null;
+ foreach($questtypes as $index => &$questtype)
+ {
+ $questtype['selected'] = ($questtype['url'] == $this->request->getPostParam('questtype'));
+ if($questtype['selected']) {
+ $questtypeIndex = $index;
+ }
+ }
+ if(is_null($questtypeIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($questtype);
+ }
+
+ // Validate media
+ $media = null;
+ if(!empty($_FILES) && array_key_exists('media', $_FILES) && $_FILES['media']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $media = $_FILES['media'];
+
+ // Check error
+ if($media['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'error', $media['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $media['mimetype'] = \hhu\z\Utils::getMimetype($media['tmp_name'], $media['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $media['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'mimetype', $media['mimetype']);
+ }
+ elseif($media['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Create new Quest
+ if($validation === true)
+ {
+ $questId = $this->Quests->createQuest(
+ $this->Auth->getUserId(),
+ $questgroup['id'],
+ $questtypes[$questtypeIndex]['id'],
+ $title,
+ $xps,
+ $task,
+ $entryText,
+ $wrongText
+ );
+ $quest = $this->Quests->getQuestById($questId);
+
+ // Update picture
+ if(!is_null($media))
+ {
+ $questsmediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $media['name'],
+ $title,
+ $media['type'],
+ $media['tmp_name']
+ );
+ if($questsmediaId > 0) {
+ $this->Quests->setQuestmedia($quest['id'], $questsmediaId);
+ }
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array('quest', $seminary['url'], $questgroup['url'], $quest['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Create Quest');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('questtypes', $questtypes);
+ $this->set('title', $title);
+ $this->set('task', $task);
+ $this->set('entryText', $entryText);
+ $this->set('wrongText', $wrongText);
+ $this->set('xps', $xps);
+ $this->set('questtype_id', $questtype);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a Quest of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ * @param string $questUrl URL-Title of Quest
+ */
+ public function edit($seminaryUrl, $questgroupUrl, $questUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+ $questgroup['picture'] = (!is_null($questgroup['questgroupspicture_id'])) ? $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']) : null;
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+ $quest['type'] = $this->Questtypes->getQuesttypeById($quest['questtype_id']);
+
+ // Get Quest media
+ $questmedia = null;
+ if(!is_null($quest['questsmedia_id'])) {
+ $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']);
+ }
+
+ // Get Quest types
+ $questtypes = $this->Questtypes->getQuesttypes();
+ foreach($questtypes as &$questtype) {
+ $questtype['selected'] = ($questtype['id'] == $quest['questtype_id']);
+ }
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $title = $quest['title'];
+ $xps = $quest['xps'];
+ $task = $quest['task'];
+ $entryText = $quest['entry_text'];
+ $wrongText = $quest['wrong_text'];
+ $fields = array('title', 'xps');
+ $validation = array();
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && (!is_null($this->request->getPostParam('edit')) || !is_null($this->request->getPostParam('edit-task'))))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Quests->questTitleExists($title, $seminary['id'], $quest['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $xps = $this->request->getPostParam('xps');
+ $task = $this->request->getPostParam('task');
+ $entryText = $this->request->getPostParam('entrytext');
+ $wrongText = $this->request->getPostParam('wrongtext');
+
+ // Validate Questtype
+ $questtypeIndex = null;
+ foreach($questtypes as $index => &$questtype)
+ {
+ $questtype['selected'] = ($questtype['url'] == $this->request->getPostParam('questtype'));
+ if($questtype['selected']) {
+ $questtypeIndex = $index;
+ }
+ }
+ if(is_null($questtypeIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($questtype);
+ }
+
+ // Validate media
+ $media = null;
+ if(!empty($_FILES) && array_key_exists('media', $_FILES) && $_FILES['media']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $media = $_FILES['media'];
+
+ // Check error
+ if($media['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'error', $media['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $media['mimetype'] = \hhu\z\Utils::getMimetype($media['tmp_name'], $media['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $media['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'mimetype', $media['mimetype']);
+ }
+ elseif($media['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Edit Quest
+ if($validation === true)
+ {
+ $this->Quests->editQuest(
+ $quest['id'],
+ $questtypes[$questtypeIndex]['id'],
+ $title,
+ $xps,
+ $task,
+ $entryText,
+ $wrongText
+ );
+ $quest = $this->Quests->getQuestById($quest['id']);
+
+ // Update picture
+ if(!is_null($media))
+ {
+ $questsmediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $media['name'],
+ $title,
+ $media['type'],
+ $media['tmp_name']
+ );
+ if($questsmediaId > 0) {
+ $this->Quests->setQuestmedia($quest['id'], $questsmediaId);
+ }
+ }
+
+ // Redirect
+ if(!is_null($this->request->getPostParam('edit-task'))) {
+ // To task editing
+ $this->redirect($this->linker->link(array('edittask', $seminary['url'], $questgroup['url'], $quest['url']), 1));
+ }
+ else {
+ // To entry
+ $this->redirect($this->linker->link(array('quest', $seminary['url'], $questgroup['url'], $quest['url']), 1));
+ }
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Quest');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('media', $questmedia);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('questtypes', $questtypes);
+ $this->set('title', $title);
+ $this->set('task', $task);
+ $this->set('entryText', $entryText);
+ $this->set('wrongText', $wrongText);
+ $this->set('xps', $xps);
+ $this->set('questtype_id', $questtype);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edittask.
+ *
+ * Edit the task of a Quest of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ * @param string $questUrl URL-Title of Quest
+ */
+ public function edittask($seminaryUrl, $questgroupUrl, $questUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+ $questgroup['picture'] = (!is_null($questgroup['questgroupspicture_id'])) ? $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']) : null;
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Render editing task
+ $task = null;
+ $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']);
+ if(!is_null($questtype['classname'])) {
+ $task = $this->renderTaskEditing($questtype['classname'], $seminary, $questgroup, $quest);
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Quest task');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('task', $task);
+ }
+
+
+ /**
+ * Action: edittexts.
+ *
+ * Edit the texts of a Quest of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ * @param string $questUrl URL-Title of Quest
+ */
+ public function edittexts($seminaryUrl, $questgroupUrl, $questUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']);
+ $questgroup['picture'] = (!is_null($questgroup['questgroupspicture_id'])) ? $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']) : null;
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Get Questtexts
+ $questtextTypes = $this->Questtexts->getQuesttexttypes();
+ foreach($questtextTypes as &$questtextType)
+ {
+ $questtextType['texts'] = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtextType['url']);
+ foreach($questtextType['texts'] as &$questtext)
+ {
+ if(!is_null($questtext['questsmedia_id'])) {
+ $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']);
+ }
+ }
+ }
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Check request method
+ $validations = array();
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ $media = $_FILES['media'];
+ $texts = $this->request->getPostParam('questtexts');
+ $deleteTexts = $this->request->getPostParam('deletes');
+ if(!is_array($deleteTexts)) {
+ $deleteTexts = array();
+ }
+
+ foreach($questtextTypes as &$type)
+ {
+ // Edit or delete texts
+ if(!array_key_exists($type['url'], $texts)) {
+ continue;
+ }
+ foreach($type['texts'] as &$text)
+ {
+ if(array_key_exists($type['url'], $deleteTexts) && array_key_exists($text['id'], $deleteTexts[$type['url']]))
+ {
+ // Delete text
+ $this->Questtexts->deleteQuesttext($text);
+ //unset($texts[$type['url']][$text['id']]);
+ }
+ elseif(array_key_exists($type['url'], $texts) && array_key_exists($text['id'], $texts[$type['url']]))
+ {
+ // Edit text
+ $this->Questtexts->editQuesttext($text['id'], $texts[$type['url']][$text['id']]);
+
+ // Validate medium
+ $medium = null;
+ $validation = true;
+ if(!empty($media) && array_key_exists($type['url'], $media['error']) && array_key_exists($text['id'], $media['error'][$type['url']]) && $media['error'][$type['url']][$text['id']] != UPLOAD_ERR_NO_FILE)
+ {
+ $medium = array(
+ 'name' => $media['name'][$type['url']][$text['id']],
+ 'tmp_name' => $media['tmp_name'][$type['url']][$text['id']],
+ 'type' => $media['type'][$type['url']][$text['id']],
+ 'size' => $media['size'][$type['url']][$text['id']],
+ 'error' => $media['error'][$type['url']][$text['id']]
+ );
+
+ // Check error
+ if($medium['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'error', $medium);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $medium['mimetype'] = \hhu\z\Utils::getMimetype($medium['tmp_name'], $medium['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $medium['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'mimetype', $medium['mimetype']);
+ }
+ elseif($medium['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'size', $mediaMimetype['size']);
+ }
+ }
+ if($validation !== true) {
+ $validations[$type['url']][$text['id']] = $validation;
+ }
+
+ // Upload medium
+ if(!is_null($medium) && $validation === true)
+ {
+ $questsmediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('questtext-%d-%d', $quest['id'], $text['id']),
+ $medium['name'],
+ $medium['type'],
+ $medium['tmp_name']
+ );
+ if($questsmediaId > 0) {
+ $this->Questtexts->setQuestmedia($text['id'], $questsmediaId);
+ }
+ }
+
+ // Remove text from list
+ //unset($texts[$type['url']][$text['id']]);
+ }
+ }
+
+ // Add new text
+ if(array_key_exists($type['url'], $texts) && array_key_exists('new', $texts[$type['url']]))
+ {
+ $text = $texts[$type['url']]['new'];
+ if(!empty($text))
+ {
+ // Add text
+ $questtextId = $this->Questtexts->addQuesttextToQuest(
+ $this->Auth->getUserId(),
+ $quest['id'],
+ $type['url'],
+ $text
+ );
+
+ // Validate medium
+ $medium = null;
+ $validation = true;
+ if(!empty($media) && array_key_exists($type['url'], $media['error']) && array_key_exists('new', $media['error'][$type['url']]) && $media['error'][$type['url']]['new'] != UPLOAD_ERR_NO_FILE)
+ {
+ $medium = array(
+ 'name' => $media['name'][$type['url']]['new'],
+ 'tmp_name' => $media['tmp_name'][$type['url']]['new'],
+ 'type' => $media['type'][$type['url']]['new'],
+ 'size' => $media['size'][$type['url']]['new'],
+ 'error' => $media['error'][$type['url']]['new']
+ );
+
+ // Check error
+ if($medium['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'error', $medium);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $medium['mimetype'] = \hhu\z\Utils::getMimetype($medium['tmp_name'], $medium['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $medium['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'mimetype', $medium['mimetype']);
+ }
+ elseif($medium['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'media', 'size', $mediaMimetype['size']);
+ }
+ }
+ if($validation !== true) {
+ $validations[$type['url']]['new'] = $validation;
+ }
+
+ // Upload medium
+ if(!is_null($medium) && $validation === true)
+ {
+ $questsmediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('questtext-%d-%d', $quest['id'], $questtextId),
+ $medium['name'],
+ $medium['type'],
+ $medium['tmp_name']
+ );
+ if($questsmediaId > 0) {
+ $this->Questtexts->setQuestmedia($questtextId, $questsmediaId);
+ }
+ }
+ }
+ }
+
+ // Reload texts
+ $type['texts'] = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $type['url']);
+ foreach($type['texts'] as &$questtext)
+ {
+ if(!is_null($questtext['questsmedia_id'])) {
+ $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']);
+ }
+ }
+ }
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit Quest texts');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('questtexttypes', $questtextTypes);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validations', $validations);
+ }
+
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Quest of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $questgroupUrl URL-Title of Questgroup
+ * @param string $questUrl URL-Title of Quest
+ */
+ public function delete($seminaryUrl, $questgroupUrl, $questUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+ $questgroup['picture'] = null;
+ if(!is_null($questgroup['questgroupspicture_id'])) {
+ $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']);
+ }
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Quests->deleteQuest($quest['id']);
+
+ // Redirect to Questgroup
+ $this->redirect($this->linker->link(array('questgroups', 'questgroup', $seminary['url'], $questgroup['url'])));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('quest', $seminary['url'], $questgroup['url'], $quest['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete Quest');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ }
+
+
+
+
+ /**
+ * Render and handle the task of a Quest.
+ *
+ * @param string $questtypeClassname Name of the class for the Questtype of a Quest
+ * @param array $seminary Seminary data
+ * @param array $questgroup Questgroup data
+ * @param array $quest Quest data
+ * @param array $character Character data
+ * @return string Rendered output
+ */
+ private function renderTask($questtypeClassname, $seminary, $questgroup, $quest, $character)
+ {
+ $task = null;
+ try {
+ // Generate request and response
+ $request = clone $this->request;
+ $response = $this->createQuesttypeResponse('quest', $seminary, $questgroup, $quest, $character);
+
+ // Load Questtype Agent
+ $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response);
+
+ // Solve Quest
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit')))
+ {
+ // Get user answers
+ $answers = $this->request->getPostParam('answers');
+
+ // Save answers in database
+ try {
+ if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) {
+ $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers);
+ }
+
+ // Match answers with correct ones
+ $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers);
+ if($status === true)
+ {
+ // Mark Quest as solved
+ $this->Quests->setQuestSolved($quest['id'], $character['id']);
+
+ // Notify of XP-level change
+ $newXPLevel = $this->Xplevels->getXPLevelById($character['xplevel_id']);
+ if($newXPLevel['level'] > $character['xplevel']) {
+ $this->Notification->addNotification(
+ \hhu\z\controllers\components\NotificationComponent::TYPE_LEVELUP,
+ $newXPLevel['level'],
+ $this->linker->link(array('characters', 'character', $seminary['url'], $character['url']))
+ );
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array(), 5, true, array('status'=>'solved'), false, 'task'));
+ }
+ elseif($status === false)
+ {
+ // Mark Quest as unsolved
+ $this->Quests->setQuestUnsolved($quest['id'], $character['id']);
+
+ // Redirect
+ $this->redirect($this->linker->link(array(), 5, true, array('status'=>'unsolved'), false, 'task'));
+ }
+ else {
+ // Mark Quest as submitted
+ $this->Quests->setQuestSubmitted($quest['id'], $character['id']);
+
+ // Send notification mail
+ $this->sendSubmissionMail($seminary, $questgroup, $quest, $character);
+
+ // Redirect
+ $this->redirect($this->linker->link(array(), 5, true, null, false, 'task'));
+ }
+ }
+ catch(\hhu\z\exceptions\SubmissionNotValidException $e) {
+ $response->addParam($e);
+ }
+ }
+
+ // Render Task
+ $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+
+
+ // Return rendered output
+ return $task;
+ }
+
+
+ /**
+ * Render and handle a Character submission for a Quest.
+ *
+ * @param string $questtypeClassname Name of the class for the Questtype of a Quest
+ * @param array $seminary Seminary data
+ * @param array $questgroup Questgroup data
+ * @param array $quest Quest data
+ * @param array $character Character data
+ * @return string Rendered output
+ */
+ private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character)
+ {
+ $task = null;
+ try {
+ // Generate request and response
+ $request = clone $this->request;
+ $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character);
+
+ // Load Questtype Agent
+ $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response);
+
+ // Solve Quest
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit')))
+ {
+ // Set status
+ if($this->request->getPostParam('submit') == _('solved'))
+ {
+ // Mark Quest as solved
+ $this->Quests->setQuestSolved($quest['id'], $character['id']);
+ }
+ else
+ {
+ // Mark Quest as unsolved
+ $this->Quests->setQuestUnsolved($quest['id'], $character['id']);
+ }
+
+ // Save additional data for Character answers
+ $questtypeAgent->controller->saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $this->request->getPostParam('characterdata'));
+
+ // Send notification
+ $this->sendSubmissionApprovedMail($character, $seminary, $questgroup, $quest);
+
+ // Redirect
+ $this->redirect($this->linker->link(array('submissions', $seminary['url'], $questgroup['url'], $quest['url']), 1));
+ }
+
+ // Render task submissions
+ $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+
+
+ // Return rendered output
+ return $task;
+ }
+
+
+ /**
+ * Render editing of a Quest.
+ *
+ * @param string $questtypeClassname Name of the class for the Questtype of a Quest
+ * @param array $seminary Seminary data
+ * @param array $questgroup Questgroup data
+ * @param array $quest Quest data
+ * @return string Rendered output
+ */
+ private function renderTaskEditing($questtypeClassname, $seminary, $questgroup, $quest)
+ {
+ $task = null;
+ try {
+ // Generate request and response
+ $request = clone $this->request;
+ $response = $this->createQuesttypeResponse('edittask', $seminary, $questgroup, $quest);
+
+ // Load Questtype Agent
+ $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response);
+
+ // Render Task
+ $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+
+
+ // Return rendered output
+ return $task;
+ }
+
+
+ /**
+ * Create a response for the Questtype rendering.
+ *
+ * @param string $action Action to run
+ * @param mixed $param Additional parameters to add to the response
+ * @return \nre\core\Response Generated response
+ */
+ private function createQuesttypeResponse($action, $param1)
+ {
+ // Clone current response
+ $response = clone $this->response;
+ // Clear parameters
+ $response->clearParams(1);
+
+ // Add Action
+ $response->addParams(
+ null,
+ $action
+ );
+
+ // Add additional parameters
+ foreach(array_slice(func_get_args(), 1) as $param) {
+ $response->addParam($param);
+ }
+
+
+ // Return response
+ return $response;
+ }
+
+
+ /**
+ * Load and construct the QuesttypeAgent for a Questtype.
+ *
+ * @param string $questtypeClassname Name of the class for the Questtype of a Quest
+ * @param \nre\core\Request $request Request
+ * @param \nre\core\Response $response Response
+ * @return \hhu\z\agents\QuesttypeAgent Questtype Agent
+ */
+ private function loadQuesttypeAgent($questtypeClassname, $request, $response)
+ {
+ // Load Agent
+ \hhu\z\agents\QuesttypeAgent::load($questtypeClassname);
+
+
+ // Construct and return Agent
+ return \hhu\z\agents\QuesttypeAgent::factory($questtypeClassname, $request, $response);
+ }
+
+
+ /**
+ * Run and render the Agent for a QuesttypeAgent and return ist output.
+ *
+ * @param \nre\core\Agent $questtypeAgent QuesttypeAgent to run and render
+ * @param \nre\core\Request $request Request
+ * @param \nre\core\Response $response Response
+ * @return string Rendered output
+ */
+ private function runQuesttypeAgent($questtypeAgent, $request, $response)
+ {
+ // Run Agent
+ $questtypeAgent->run($request, $response);
+
+
+ // Render and return output
+ return $questtypeAgent->render();
+ }
+
+
+ /**
+ * Send mail for new Character submission.
+ *
+ * @param array $seminary Seminary which the Quest belongs to
+ * @param array $questgroup Questgroup of Quest
+ * @param array $quest Quest the answer has been submitted for
+ * @param array $character Character that send the submission
+ */
+ private function sendSubmissionMail($seminary, $questgroup, $quest, $character)
+ {
+ // Get system moderators
+ $moderators = $this->Characters->getCharactersWithCharacterRole($seminary['id'], 'moderator');
+
+ // Send notification mail
+ try {
+ foreach($moderators as &$moderator)
+ {
+ $user = $this->Users->getUserById($moderator['user_id']);
+ if($user['mailing']) {
+ \hhu\z\Utils::sendMail(
+ $user['email'],
+ 'charactersubmission',
+ true,
+ array(
+ $user,
+ $seminary,
+ $questgroup,
+ $quest,
+ $character
+ ),
+ $this->linker
+ );
+ }
+ }
+ }
+ catch(\hhu\z\exceptions\MailingException $e) {
+ $this->log($e->getMessage());
+ }
+ }
+
+
+ /**
+ * Send mail for approval of a Character submission.
+ *
+ * @param array $character Character of submission that has been approved
+ * @param array $seminary Seminary which the Quest belongs to
+ * @param array $questgroup Questgroup of Quest
+ * @param array $quest Quest the submission has been approved for
+ */
+ private function sendSubmissionApprovedMail($character, $seminary, $questgroup, $quest)
+ {
+ // Get user
+ $user = $this->Users->getUserById($character['user_id']);
+ if(!$user['mailing']) {
+ return;
+ }
+
+ // Send notification mail
+ try {
+ \hhu\z\Utils::sendMail(
+ $user['email'],
+ 'charactersubmissionapproved',
+ true,
+ array(
+ $user,
+ $seminary,
+ $questgroup,
+ $quest
+ ),
+ $this->linker
+ );
+ }
+ catch(\hhu\z\exceptions\MailingException $e) {
+ $this->log($e->getMessage());
+ }
+ }
+
+ }
+
+?>
diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc
new file mode 100644
index 00000000..95c41cab
--- /dev/null
+++ b/controllers/SeminariesController.inc
@@ -0,0 +1,602 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to list registered seminaries.
+ *
+ * @author Oliver Hanraths
+ */
+ class SeminariesController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('users', 'seminaries', 'characterroles', 'charactertypes', 'xplevels', 'questgroupshierarchy', 'questgroups', 'questgrouptexts', 'quests', 'questtexts', 'media');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'seminary' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'copy' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user'),
+ 'calculatexps' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'seminary' => array('admin', 'moderator', 'user', 'guest'),
+ 'edit' => array('admin'),
+ 'copy' => array('admin'),
+ 'delete' => array('admin'),
+ 'calculatexps' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List registered seminaries.
+ */
+ public function index()
+ {
+ // Get seminaries
+ $seminaries = $this->Seminaries->getSeminaries();
+
+ // Get additional data
+ foreach($seminaries as &$seminary)
+ {
+ $description = \hhu\z\Utils::shortenString($seminary['description'], 100, 120);
+ $seminary['description'] = $description.(strlen($description) < strlen($seminary['description']) ? ' …' : null);
+ $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']);
+ $seminary['charactertypes'] = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']);
+ $seminary['xplevels'] = $this->Xplevels->getXPLevelsForSeminary($seminary['id']);
+
+ // Character of currently logged-in user
+ try {
+ $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+ $seminary['usercharacter']['characterroles'] = $this->Characterroles->getCharacterrolesForCharacterById($seminary['usercharacter']['id']);
+ $seminary['xps'] = $this->Seminaries->getTotalXPs($seminary['id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ }
+
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Seminaries');
+
+ // Pass data to view
+ $this->set('seminaries', $seminaries);
+ }
+
+
+ /**
+ * Action: seminary.
+ *
+ * Show a seminary and its details.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a seminary
+ */
+ public function seminary($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Created user
+ $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']);
+
+ // Get Character
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+
+ // Questgrouphierarchy and Questgroups
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminary['id']);
+ foreach($questgroupshierarchy as &$hierarchy)
+ {
+ // Get Questgroups
+ $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']);
+
+ // Get additional data
+ foreach($hierarchy['questgroups'] as $i => &$questgroup)
+ {
+ // Check permission of Questgroups
+ if($i >= 1 && count(array_intersect(array('admin','moderator'), SeminaryController::$character['characterroles'])) == 0)
+ {
+ if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']))
+ {
+ $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i);
+ break;
+ }
+ }
+
+ // Get first Questgroup text
+ $text = $this->getFirstQuestgroupText($questgroup['id']);
+ if(!is_null($text))
+ {
+ $questgroup['text'] = \hhu\z\Utils::shortenString($text, 100, 120).' …';
+ }
+
+ // Get Character XPs
+ $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']);
+
+ // Get Media
+ $questgroup['picture'] = null;
+ try {
+ $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ }
+ }
+ }
+
+
+ // Set titile
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroupshierarchy', $questgroupshierarchy);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Seminary.
+ */
+ public function create()
+ {
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $title = '';
+ $course = '';
+ $description = '';
+ $fields = array('title', 'course');
+ $validation = array();
+
+ // Create a new Seminary
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Seminaries->seminaryTitleExists($title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $course = $this->request->getPostParam('course');
+ $description = $this->request->getPostParam('description');
+
+ // Validate moodpic
+ $moodpic = null;
+ if(!empty($_FILES) && array_key_exists('moodpic', $_FILES) && $_FILES['moodpic']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $moodpic = $_FILES['moodpic'];
+
+ // Check error
+ if($moodpic['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'error', $moodpic['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $moodpic['mimetype'] = \hhu\z\Utils::getMimetype($moodpic['tmp_name'], $moodpic['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $moodpic['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'mimetype', $moodpic['mimetype']);
+ }
+ elseif($moodpic['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Create new Seminary
+ if($validation === true)
+ {
+ $seminaryId = $this->Seminaries->createSeminary(
+ $this->Auth->getUserId(),
+ $title,
+ $course,
+ $description
+ );
+ $seminary = $this->Seminaries->getSeminaryById($seminaryId);
+
+ // Upload moodpic
+ if(!is_null($moodpic))
+ {
+ $mediaId = $this->Media->createMoodpic(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('seminarymoodpic-%s', $seminary['url']),
+ '',
+ $moodpic['mimetype'],
+ $moodpic['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Seminaries->setMoodpicForSeminary($seminary['id'], $mediaId);
+ }
+ }
+
+ // Redirect to Seminary overview
+ $this->redirect($this->linker->link('index', 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('New seminary');
+
+ // Pass data to view
+ $this->set('title', $title);
+ $this->set('course', $course);
+ $this->set('description', $description);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a seminary
+ */
+ public function edit($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['moodpics'];
+
+ // Values
+ $title = $seminary['title'];
+ $course = $seminary['course'];
+ $description = $seminary['description'];
+ $fields = array('title', 'course');
+ $validation = array();
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Seminaries->seminaryTitleExists($title, $seminary['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $course = $this->request->getPostParam('course');
+ $description = $this->request->getPostParam('description');
+
+ // Validate moodpic
+ $moodpic = null;
+ if(!empty($_FILES) && array_key_exists('moodpic', $_FILES) && $_FILES['moodpic']['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $moodpic = $_FILES['moodpic'];
+
+ // Check error
+ if($moodpic['error'] !== UPLOAD_ERR_OK) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'error', $moodpic['error']);
+ }
+
+ // Check mimetype
+ $mediaMimetype = null;
+ $moodpic['mimetype'] = \hhu\z\Utils::getMimetype($moodpic['tmp_name'], $moodpic['type']);
+ foreach($mimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $moodpic['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'mimetype', $moodpic['mimetype']);
+ }
+ elseif($moodpic['size'] > $mediaMimetype['size']) {
+ $validation = $this->Validation->addValidationResult($validation, 'moodpic', 'size', $mediaMimetype['size']);
+ }
+ }
+
+ // Edit Seminary
+ if($validation === true)
+ {
+ $this->Seminaries->editSeminary(
+ $seminary['id'],
+ $title,
+ $course,
+ $description
+ );
+ $seminary = $this->Seminaries->getSeminaryById($seminary['id']);
+
+ // Update moodpic
+ if(!is_null($moodpic))
+ {
+ $mediaId = $this->Media->createMoodpic(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('seminarymoodpic-%s', $seminary['url']),
+ '',
+ $moodpic['mimetype'],
+ $moodpic['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Seminaries->setMoodpicForSeminary($seminary['id'], $mediaId);
+ }
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('seminary', $seminary['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit seminary');
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('title', $title);
+ $this->set('course', $course);
+ $this->set('description', $description);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Copy a Seminary and selected content.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a seminary
+ */
+ public function copy($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Values
+ $title = sprintf('%s (%s)', $seminary['title'], _('Copy'));
+ $course = $seminary['course'];
+ $description = $seminary['description'];
+ $elements = array();
+ $fields = array('title', 'course');
+ $validation = array();
+ $exception = null;
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('edit')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $title = $this->request->getPostParam('title');
+ if($this->Seminaries->seminaryTitleExists($title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $course = $this->request->getPostParam('course');
+ $description = $this->request->getPostParam('description');
+ $elements = $this->request->getPostParam('elements');
+ if(!is_array($elements)) {
+ $elements = array();
+ }
+
+ // Copy Seminary
+ if($validation === true)
+ {
+ try {
+ $seminaryId = $this->Seminaries->copySeminary(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $title,
+ $course,
+ $description,
+ array_key_exists('seminaryfields', $elements),
+ array_key_exists('media', $elements),
+ array_key_exists('questgroupshierarchy', $elements),
+ array_key_exists('questgroups', $elements),
+ array_key_exists('quests', $elements),
+ array_key_exists('questtopics', $elements),
+ array_key_exists('charactertypes', $elements),
+ array_key_exists('xplevels', $elements),
+ array_key_exists('avatars', $elements),
+ array_key_exists('achievements', $elements),
+ array_key_exists('charactergroupsgroups', $elements),
+ array_key_exists('charactergroupsquests', $elements),
+ array_key_exists('map', $elements)
+ );
+ $seminary = $this->Seminaries->getSeminaryById($seminaryId);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link('index', 1));
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) {
+ $exception = $e;
+ }
+ catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) {
+ $exception = $e;
+ }
+ catch(\hhu\z\exceptions\FileCopyException $e) {
+ $exception = $e;
+ }
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('title', $title);
+ $this->set('course', $course);
+ $this->set('description', $description);
+ $this->set('elements', $elements);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ $this->set('exception', $exception);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a seminary
+ */
+ public function delete($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Seminaries->deleteSeminary($seminary['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(null, 1));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('seminary', $seminary['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete seminary');
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ }
+
+
+ /**
+ * Action: calculatexps.
+ *
+ * (Re-) Calculate the amount of achievable XPs for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function calculatexps($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // (Re-) Calculate XPs
+ $this->Seminaries->calculateXPsForSeminary($seminary['id']);
+
+ // Redirect to Questgroup
+ $this->redirect($this->linker->link(array('seminary', $seminary['url']), 1));
+ }
+
+
+
+
+ /**
+ * Get the first text of a Questgroup.
+ *
+ * @param int $questgroupId ID of a Questgroup
+ * @return string First text of this Questgroup or NULL
+ */
+ private function getFirstQuestgroupText($questgroupId)
+ {
+ // Text of Questgroup itself
+ $questgroupTexts = $this->Questgrouptexts->getQuestgroupTexts($questgroupId);
+ if(!empty($questgroupTexts)) {
+ return $questgroupTexts[0]['text'];
+ }
+
+ // Text of first Quest
+ $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroupId);
+ if(!is_null($quest))
+ {
+ $questText = $this->Questtexts->getFirstQuestText($quest['id']);
+ if(!is_null($questText)) {
+ return $questText;
+ }
+ }
+
+ // Text of ChildQuestgroups
+ $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroupId);
+ $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['id']);
+ foreach($childQuestgroupshierarchy as &$hierarchy)
+ {
+ // Get Questgroups
+ $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroupId);
+ foreach($questgroups as &$group)
+ {
+ $childQuestgroupText = $this->getFirstQuestgroupText($group['id']);
+ if(!is_null($childQuestgroupText)) {
+ return $childQuestgroupText;
+ }
+ }
+ }
+
+
+ // No text found
+ return null;
+ }
+
+ }
+
+?>
diff --git a/controllers/SeminarybarController.inc b/controllers/SeminarybarController.inc
new file mode 100644
index 00000000..7a987c37
--- /dev/null
+++ b/controllers/SeminarybarController.inc
@@ -0,0 +1,89 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to display a sidebar with Seminary related
+ * information.
+ *
+ * @author Oliver Hanraths
+ */
+ class SeminarybarController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('characters', 'quests', 'questgroups', 'achievements', 'charactergroups', 'avatars', 'media', 'xplevels');
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index()
+ {
+ // Do not render at first
+ $this->set('render', false);
+
+ // Get Seminary
+ $seminary = SeminaryController::$seminary;
+ if(is_null($seminary)) {
+ return;
+ }
+
+ // Get Character
+ $character = SeminaryController::$character;
+ if(is_null($character)) {
+ return;
+ }
+ $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']);
+
+ // Get “last” Quest
+ $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']);
+ if(!is_null($lastQuest)) {
+ $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']);
+ }
+
+ // Get last achieved Achievement
+ $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id'], false);
+ $lastAchievement = array_shift($achievements);
+
+ // Get Character group members
+ $characterGroups = array();
+ foreach($this->Charactergroups->getGroupsForCharacter($character['id']) as $group)
+ {
+ $groupsgroup = $this->Charactergroups->getGroupsgroupById($group['charactergroupsgroup_id']);
+ if($groupsgroup['preferred'])
+ {
+ $group['members'] = $this->Characters->getCharactersForGroup($group['id']);
+ $characterGroups[] = $group;
+ }
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('character', $character);
+ $this->set('lastQuest', $lastQuest);
+ $this->set('lastAchievement', $lastAchievement);
+ $this->set('characterGroups', $characterGroups);
+ // Render now
+ $this->set('render', true);
+ }
+
+ }
+
+?>
diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc
new file mode 100644
index 00000000..66c81f59
--- /dev/null
+++ b/controllers/SeminarymenuController.inc
@@ -0,0 +1,65 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to display a menu with Seminary related
+ * links.
+ *
+ * @author Oliver Hanraths
+ */
+ class SeminarymenuController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('map');
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Set userdata
+ $this->set('loggedUser', \hhu\z\controllers\IntermediateController::$user);
+ $this->set('loggedSeminary', \hhu\z\controllers\SeminaryController::$seminary);
+ $this->set('loggedCharacter', \hhu\z\controllers\SeminaryController::$character);
+ }
+
+
+ /**
+ * Action: index.
+ */
+ public function index()
+ {
+ // Get map of Seminary
+ $map = $this->Map->getMapOfSeminary(\hhu\z\controllers\SeminaryController::$seminary['id']);
+
+
+ // Pass data to view
+ $this->set('map', $map);
+ }
+
+ }
+
+?>
diff --git a/controllers/TextmailController.inc b/controllers/TextmailController.inc
new file mode 100644
index 00000000..99a8fa1b
--- /dev/null
+++ b/controllers/TextmailController.inc
@@ -0,0 +1,55 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the TextmailAgent for generating a simple text-mail
+ * message.
+ *
+ * @author Oliver Hanraths
+ */
+ class TextmailController extends \nre\core\Controller
+ {
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+
+ // Set linker
+ $this->set('linker', ($request instanceof \hhu\z\requests\MailRequest && !is_null($request->getLinker())) ? $request->getLinker() : null);
+ }
+
+
+ /**
+ * Action: index.
+ *
+ * Create simple text-mail message.
+ */
+ public function index()
+ {
+ $this->set('appname', \nre\configs\AppConfig::$app['name']);
+ }
+
+ }
+
+?>
diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc
new file mode 100644
index 00000000..3468a6d5
--- /dev/null
+++ b/controllers/UploadsController.inc
@@ -0,0 +1,318 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the UploadsAgent to process and show user uploads.
+ *
+ * @author Oliver Hanraths
+ */
+ class UploadsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('uploads', 'users', 'userroles', 'characterroles', 'seminaries', 'charactergroups');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'seminary' => array('admin', 'moderator', 'user', 'guest'),
+ 'charactergroup' => array('admin', 'moderator', 'user', 'guest')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'seminary' => array('admin', 'moderator', 'user', 'guest'),
+ 'charactergroup' => array('admin', 'moderator', 'user')
+ );
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param \nre\core\Request $request Current request
+ * @param \nre\core\Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Set headers for caching control
+ $response->addHeader("Pragma: public");
+ $response->addHeader("Cache-control: public, max-age=".(60*60*24));
+ $response->addHeader("Expires: ".gmdate('r', time()+(60*60*24)));
+ $response->addHeader("Date: ".gmdate(\DateTime::RFC822));
+ }
+
+
+ /**
+ * Action: seminary.
+ *
+ * Display a Seminary upload.
+ *
+ * @throws \nre\exceptions\AccessDeniedException
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $uploadUrl URL-name of the upload
+ * @param string $action Current action (optional)
+ */
+ public function seminary($seminaryUrl, $uploadUrl, $action=null)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Upload
+ $upload = $this->Uploads->getSeminaryuploadByUrl($seminary['id'], $uploadUrl);
+
+ // Check permissions
+ if(!$upload['public'])
+ {
+ $user = $this->Users->getUserById($this->Auth->getUserId());
+ $user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById($user['id']));
+
+ // System roles
+ if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0)
+ {
+ // Owner of file
+ if($upload['created_user_id'] != $user['id'])
+ {
+ // Seminary permissions
+ $characterRoles = array_map(function($r) { return $r['name']; }, $this->Characterroles->getCharacterrolesForCharacterById($character['id']));
+ if(count(array_intersect(array('admin', 'moderator'), $characterRoles)) == 0) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ }
+ }
+
+ // Get file
+ switch($action)
+ {
+ case null:
+ $file = $this->getUploadFile($upload);
+ break;
+ case 'thumbnail':
+ $file = $this->createThumbnail($upload);
+ break;
+ default:
+ throw new \nre\exceptions\ParamsNotValidException($action);
+ break;
+ }
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('upload', $upload);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactergroup.
+ *
+ * Display the icon of a Character group.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $groupUrl URL-Title of a Character group
+ */
+ public function charactergroup($seminaryUrl, $groupsgroupUrl, $groupUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character group
+ $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl);
+
+ // Get Upload
+ $upload = $this->Uploads->getSeminaryuploadById($group['seminaryupload_id']);
+
+ // Get file
+ $file = $this->getUploadFile($upload);
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('upload', $upload);
+ $this->set('file', $file);
+ }
+
+
+
+
+ /**
+ * Determine the file for an upload.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param array $upload Upload to get file for
+ * @return object File for the upload (or null if upload is cached)
+ */
+ private function getUploadFile($upload)
+ {
+ // Set content-type
+ $this->response->addHeader("Content-type: ".$upload['mimetype']."");
+
+ // Set filename
+ $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminaryuploads'].DS.$upload['url'];
+ if(!file_exists($upload['filename'])) {
+ throw new \nre\exceptions\IdNotFoundException($uploadUrl);
+ }
+
+ // Cache
+ if($this->setCacheHeaders($upload['filename'])) {
+ return null;
+ }
+
+
+ return file_get_contents($upload['filename']);
+ }
+
+
+ /**
+ * Create a thumbnail from an upload.
+ *
+ * @param array $upload Upload to create thumbnail for
+ * @return object Thumbnail for the upload (or null if thumbnail is cached)
+ */
+ private function createThumbnail($upload)
+ {
+ // Set filename
+ $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminaryuploads'].DS.$upload['url'];
+
+ // Set content-type
+ $this->response->addHeader("Content-type: image/jpeg");
+
+ // Check file
+ if(!file_exists($upload['filename'])) {
+ throw new \nre\exceptions\IdNotFoundException($upload['url']);
+ }
+
+ // Cache
+ if($this->setCacheHeaders($upload['filename'])) {
+ return null;
+ }
+
+ // Set geometry
+ $width = 100;
+ $height = 100;
+
+ switch($upload['mimetype'])
+ {
+ case 'image/jpeg':
+ case 'image/png':
+ // Read image from cache
+ $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.$upload['url'].'-'.$width.'x'.$height;
+ if(file_exists($tempFileName))
+ {
+ // Check age of file
+ if(date('r', filemtime($tempFileName)+(60*60*24)) > date('r', time())) {
+ // Too old, delete
+ unlink($tempFileName);
+ }
+ else {
+ // Valid, read and return
+ return file_get_contents($tempFileName);
+ }
+ }
+
+ // ImageMagick
+ $im = new \Imagick($upload['filename']);
+
+ // Calculate new size
+ $geometry = $im->getImageGeometry();
+ if($geometry['width'] < $width) {
+ $width = $geometry['width'];
+ }
+ if($geometry['height'] < $height) {
+ $height = $geometry['width'];
+ }
+
+ // Process
+ $im->thumbnailImage($width, $height, true);
+ $im->contrastImage(1);
+ $im->setImageFormat('jpeg');
+
+ // Save temporary file
+ $im->writeImage($tempFileName);
+
+
+ // Return resized image
+ return $im;
+ break;
+ default:
+ throw new \nre\exceptions\ParamsNotValidException('thumbnail');
+ break;
+ }
+
+
+ return $this->getUploadFile($upload);
+ }
+
+
+ /**
+ * Determine file information and set the HTTP-header for
+ * caching accordingly.
+ *
+ * @param string $fileName Filename
+ * @return boolean HTTP-status 304 was set (in cache)
+ */
+ private function setCacheHeaders($fileName)
+ {
+ // Determine last change of file
+ $fileLastModified = gmdate('r', filemtime($fileName));
+
+ // Generate E-Tag
+ $fileEtag = hash('sha256', $fileLastModified.$fileName);
+
+
+ // Set header
+ $this->response->addHeader("Last-Modified: ".$fileLastModified);
+ $this->response->addHeader("Etag: ".$fileEtag);
+ // HTTP-status
+ $headerModifiedSince = $this->request->getServerParam('HTTP_IF_MODIFIED_SINCE');
+ $headerNoneMatch = $this->request->getServerParam('HTTP_IF_NONE_MATCH');
+ if(
+ !is_null($headerModifiedSince) && $fileLastModified < strtotime($headerModifiedSince) &&
+ !is_null($headerNoneMatch) && $headerNoneMatch == $fileEtag
+ ) {
+ $this->response->setExit(true);
+ $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304));
+
+ return true;
+ }
+
+
+ return false;
+ }
+
+ }
+
+?>
diff --git a/controllers/UserrolesController.inc b/controllers/UserrolesController.inc
new file mode 100644
index 00000000..ee2f71a0
--- /dev/null
+++ b/controllers/UserrolesController.inc
@@ -0,0 +1,47 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to display and manage userroles.
+ *
+ * @author Oliver Hanraths
+ */
+ class UserrolesController extends \hhu\z\Controller
+ {
+
+
+
+
+ /**
+ * Action: user.
+ *
+ * Show a user and its details.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $userUrl URL-Username of an user
+ */
+ public function user($userUrl)
+ {
+ // Get userroles
+ $roles = $this->Userroles->getUserrolesForUserByUrl($userUrl);
+
+
+ // Pass data to view
+ $this->set('roles', $roles);
+ }
+
+
+ }
+
+?>
diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc
new file mode 100644
index 00000000..23f696c3
--- /dev/null
+++ b/controllers/UsersController.inc
@@ -0,0 +1,641 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the Agent to list registered users and their data.
+ *
+ * @author Oliver Hanraths
+ */
+ class UsersController extends \hhu\z\controllers\IntermediateController
+ {
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator'),
+ 'user' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin')
+ );
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('users', 'userroles', 'characters', 'characterroles', 'avatars', 'media', 'xplevels');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * @param string $all Whether to list all users at once or not (optional)
+ */
+ public function index($all=null)
+ {
+ // Set filters
+ $sortorder = 'username';
+ $username = null;
+ $name = null;
+ $email = null;
+ $page = 1;
+ if($this->request->getRequestMethod() == 'GET')
+ {
+ $sortorder = $this->request->getGetParam('sortorder');
+ $sortorder = !empty($sortorder) ? $sortorder : 'username';
+ $username = (!is_null($this->request->getGetParam('username'))) ? $this->request->getGetParam('username') : $username;
+ $name = (!is_null($this->request->getGetParam('name'))) ? $this->request->getGetParam('name') : $name;
+ $email = (!is_null($this->request->getGetParam('email'))) ? $this->request->getGetParam('email') : $email;
+ $page = $this->request->getGetParam('page');
+ $page = !empty($page) ? intval($page) : 1;
+ }
+
+ // Get registered users
+ $limit = ($all != 'all') ? \nre\configs\AppConfig::$misc['lists_limit'] : null;
+ $offset = ($all != 'all') ? max((intval($page) - 1), 0) * $limit : 0;
+ $usersCount = $this->Users->getUsersCount($username, $name, $email);
+ $users = $this->Users->getUsers($sortorder, $username, $name, $email, $limit, $offset);
+ foreach($users as &$user) {
+ $user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById($user['id']));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Users');
+
+ // Pass data to view
+ $this->set('users', $users);
+ $this->set('usersCount', $usersCount);
+ $this->set('sortorder', $sortorder);
+ $this->set('username', $username);
+ $this->set('name', $name);
+ $this->set('email', $email);
+ $this->set('all', $all);
+ $this->set('page', $page);
+ $this->set('limit', $limit);
+ }
+
+
+ /**
+ * Action: user.
+ *
+ * Show a user and its details.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @throws \nre\exceptions\AccessDeniedException
+ * @param string $userUrl URL-Username of an user
+ */
+ public function user($userUrl)
+ {
+ // Get user
+ $user = $this->Users->getUserByUrl($userUrl);
+
+ // Check permissions
+ if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Get Characters
+ $characters = $this->Characters->getCharactersForUser($user['id']);
+
+ // Additional Character information
+ foreach($characters as &$character)
+ {
+ // Seminary roles
+ $character['characterroles'] = $this->Characterroles->getCharacterrolesForCharacterById($character['id']);
+ $character['characterroles'] = array_map(function($a) { return $a['name']; }, $character['characterroles']);
+
+ // Level
+ try {
+ $character['xplevel'] = $this->Xplevels->getXPLevelById($character['xplevel_id']);
+ }
+ catch(\nre\Exceptions\IdNotFoundException $e) {
+ // No XP-level
+ }
+
+ // Avatar
+ $avatar = $this->Avatars->getAvatarById($character['avatar_id']);
+ if(!is_null($avatar['small_avatarpicture_id']))
+ {
+ //$character['seminary'] =
+ $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']);
+ }
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized($user['username']);
+ $this->addTitleLocalized('Users');
+
+ // Pass data to view
+ $this->set('user', $user);
+ $this->set('characters', $characters);
+ }
+
+
+ /**
+ * Action: login.
+ *
+ * Log in a user.
+ */
+ public function login()
+ {
+ $username = '';
+ $referrer = null;
+
+ // Log the user in
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('login')))
+ {
+ $username = $this->request->getPostParam('username');
+ $referrer = $this->request->getPostParam('referrer');
+ $userId = $this->Users->login(
+ $username,
+ $this->request->getPostParam('password')
+ );
+
+ if(!is_null($userId))
+ {
+ $this->Auth->setUserId($userId);
+ $user = $this->Users->getUserById($userId);
+
+ if(!empty($referrer)) {
+ $this->redirect($referrer);
+ }
+ else {
+ $this->redirect($this->linker->link(array($user['url']), 1));
+ }
+ }
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Login');
+
+ // Pass data to view
+ $this->set('username', $username);
+ $this->set('referrer', $referrer);
+ $this->set('failed', ($this->request->getRequestMethod() == 'POST'));
+ }
+
+
+ /**
+ * Action: register.
+ *
+ * Register a new user.
+ */
+ public function register()
+ {
+ $username = '';
+ $prename = '';
+ $surname = '';
+ $email = '';
+
+ $fields = array('username', 'prename', 'surname', 'email', 'password');
+ $validation = array();
+ $emailhost = \nre\configs\AppConfig::$app['registration_host'];
+
+ // Register a new user
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $username = $this->request->getPostParam('username');
+ if($this->Users->usernameExists($username)) {
+ $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true);
+ }
+ $prename = $this->request->getPostParam('prename');
+ $surname = $this->request->getPostParam('surname');
+ $email = $this->request->getPostParam('email');
+ if(!empty($emailhost)) {
+ if(substr_compare($email, $emailhost, max(0, strlen($email)-strlen($emailhost)), strlen($emailhost)) !== 0) {
+ $validation = $this->Validation->addValidationResult($validation, 'email', 'host', 'invalid');
+ }
+ }
+ if($this->Users->emailExists($email)) {
+ $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true);
+ }
+ $password = $this->request->getPostParam('password');
+ if($password != $this->request->getPostParam('passwordrepeat')) {
+ $validation = $this->Validation->addValidationResult($validation, 'password', 'repeat', false);
+ }
+
+
+ // Register
+ if($validation === true)
+ {
+ $userId = $this->Users->createUser(
+ $username,
+ $prename,
+ $surname,
+ $email,
+ $password
+ );
+ $user = $this->Users->getUserById($userId);
+
+ // Send mail
+ $this->sendRegistrationMail($user);
+
+ // Login
+ $this->Auth->setUserId($userId);
+
+
+ // Redirect to user page
+ $this->redirect($this->linker->link(array($user['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Registration');
+
+ // Pass data to view
+ $this->set('username', $username);
+ $this->set('prename', $prename);
+ $this->set('surname', $surname);
+ $this->set('email', $email);
+ $this->set('emailhost', $emailhost);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: logout.
+ *
+ * Log out a user.
+ */
+ public function logout()
+ {
+ // Unset the currently logged in user
+ $this->Auth->setUserId(null);
+
+ // Redirect
+ $this->redirect($this->linker->link(array()));
+ }
+
+
+ /**
+ * Action: manage.
+ *
+ * Manage users.
+ */
+ public function manage()
+ {
+ $selectedUsers = array();
+ //global $sortorder;
+ $sortorder = 'username';
+
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Set sortorder
+ $sortorder = $this->request->getPostParam('sortorder');
+
+ // Do action
+ $selectedUsers = $this->request->getPostParam('users');
+ if(!is_array($selectedUsers)) {
+ $selectedUsers = array();
+ }
+ if(!is_null($this->request->getPostParam('actions')) && count($this->request->getPostParam('actions')) > 0 && !is_null($this->request->getPostParam('users')) && count($this->request->getPostParam('users')) > 0)
+ {
+ $actions = $this->request->getPostParam('actions');
+ $action = array_keys($actions)[0];
+
+ switch($action)
+ {
+ // Add/remove role to/from Characters
+ case 'addrole':
+ case 'removerole':
+ // Determine role and check permissions
+ $role = null;
+ switch($actions[$action])
+ {
+ case _('Admin'):
+ if(!in_array('admin', \hhu\z\controllers\IntermediateController::$user['roles'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ $role = 'admin';
+ break;
+ case _('Moderator'):
+ if(!in_array('admin', \hhu\z\controllers\IntermediateController::$user['roles'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ $role = 'moderator';
+ break;
+ case _('User'):
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) <= 0) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ $role = 'user';
+ break;
+ }
+
+ // Add role
+ if($action == 'addrole') {
+ foreach($selectedUsers as &$userId) {
+ $this->Userroles->addUserroleToUser($userId, $role);
+ }
+ }
+ // Remove role
+ else {
+ foreach($selectedUsers as &$userId) {
+ $this->Userroles->removeUserroleFromUser($userId, $role);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // Get registered users
+ $users = $this->Users->getUsers($sortorder);
+ foreach($users as &$user) {
+ $user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById($user['id']));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Manage users');
+
+ // Pass data to view
+ $this->set('users', $users);
+ $this->set('selectedUsers', $selectedUsers);
+ $this->set('sortorder', $sortorder);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new user.
+ */
+ public function create()
+ {
+ // Values
+ $username = '';
+ $prename = '';
+ $surname = '';
+ $email = '';
+ $fields = array('username', 'prename', 'surname', 'email', 'password');
+ $validation = array();
+
+ // Create new user
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $username = $this->request->getPostParam('username');
+ if($this->Users->usernameExists($username)) {
+ $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true);
+ }
+ $prename = $this->request->getPostParam('prename');
+ $surname = $this->request->getPostParam('surname');
+ $email = $this->request->getPostParam('email');
+ if($this->Users->emailExists($email)) {
+ $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true);
+ }
+ $password = $this->request->getPostParam('password');
+ if($password != $this->request->getPostParam('passwordrepeat')) {
+ $validation = $this->Validation->addValidationResult($validation, 'password', 'repeat', false);
+ }
+
+ // Create
+ if($validation === true)
+ {
+ $userId = $this->Users->createUser(
+ $this->request->getPostParam('username'),
+ $this->request->getPostParam('prename'),
+ $this->request->getPostParam('surname'),
+ $this->request->getPostParam('email'),
+ $this->request->getPostParam('password')
+ );
+
+ // Redirect to user
+ $user = $this->Users->getUserById($userId);
+ $this->redirect($this->linker->link(array($user['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('New user');
+
+ // Pass data to view
+ $this->set('username', $username);
+ $this->set('prename', $prename);
+ $this->set('surname', $surname);
+ $this->set('email', $email);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a user.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $userUrl URL-Username of an user
+ */
+ public function edit($userUrl)
+ {
+ // User
+ $user = $this->Users->getUserByUrl($userUrl);
+
+ // Check permissions
+ if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != \hhu\z\controllers\IntermediateController::$user['id']) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Values
+ $username = $user['username'];
+ $prename = $user['prename'];
+ $surname = $user['surname'];
+ $email = $user['email'];
+ $mailing = $user['mailing'];
+ $fields = array('username', 'prename', 'surname', 'email');
+ $validation = array();
+
+ // Edit user
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('save')))
+ {
+ // Get params and validate them
+ $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields);
+ $username = $this->request->getPostParam('username');
+ if($this->Users->usernameExists($username, $user['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true);
+ }
+ $password = $this->request->getPostParam('password');
+ if(!empty($password))
+ {
+ $validation = $this->Validation->addValidationResults($validation,
+ 'password',
+ $this->Validation->validateParam(
+ $this->request->getPostParams(),
+ 'password'
+ )
+ );
+ if($password != $this->request->getPostParam('passwordrepeat')) {
+ $validation = $this->Validation->addValidationResult($validation, 'password', 'repeat', false);
+ }
+ }
+ $prename = $this->request->getPostParam('prename');
+ $surname = $this->request->getPostParam('surname');
+ $email = $this->request->getPostParam('email');
+ if($this->Users->emailExists($email, $user['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true);
+ }
+ $mailing = !is_null($this->request->getPostParam('mailing'));
+
+ // Save changes
+ if($validation === true)
+ {
+ // Edit user
+ $this->Users->editUser(
+ $user['id'],
+ (count(array_intersect(array('admin','moderator'),\hhu\z\controllers\IntermediateController::$user['roles'])) > 0) ? $this->request->getPostParam('username') : $user['username'],
+ $prename,
+ $surname,
+ $email,
+ $password,
+ $mailing
+ );
+
+ // Redirect to entry
+ $user = $this->Users->getUserById($user['id']);
+ $this->redirect($this->linker->link(array('user', $user['url']), 1));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+ $validationSettings['password'] = \nre\configs\AppConfig::$validation['password'];
+
+
+ // Set titile
+ $this->addTitleLocalized('Edit user');
+
+ // Pass data to view
+ $this->set('username', $username);
+ $this->set('prename', $prename);
+ $this->set('surname', $surname);
+ $this->set('email', $email);
+ $this->set('mailing', $mailing);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a user.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $userUrl URL-Username of an user
+ */
+ public function delete($userUrl)
+ {
+ // User
+ $user = $this->Users->getUserByUrl($userUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete user
+ $this->Users->deleteUser($user['id']);
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(null, 1));
+ }
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array('user', $user['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete user');
+
+ // Show confirmation
+ $this->set('user', $user);
+ }
+
+
+
+
+ /**
+ * Send mail for new user registration.
+ *
+ * @param array $user Newly registered user
+ */
+ private function sendRegistrationMail($user)
+ {
+ // Get system moderators
+ $moderators = $this->Users->getUsersWithRole('moderator');
+
+ // Send notification mail
+ try {
+ foreach($moderators as &$moderator)
+ {
+ if($moderator['mailing']) {
+ \hhu\z\Utils::sendMail(
+ $moderator['email'],
+ 'userregistration',
+ true,
+ array(
+ $moderator,
+ $user
+ ),
+ $this->linker
+ );
+ }
+ }
+ }
+ catch(\hhu\z\exceptions\MailingException $e) {
+ $this->log($e->getMessage());
+ }
+ }
+
+ }
+
+?>
diff --git a/controllers/XplevelsController.inc b/controllers/XplevelsController.inc
new file mode 100644
index 00000000..47b849d5
--- /dev/null
+++ b/controllers/XplevelsController.inc
@@ -0,0 +1,189 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers;
+
+
+ /**
+ * Controller of the XplevelsAgent to handle XP-levels of a
+ * Seminary.
+ *
+ * @author Oliver Hanraths
+ */
+ class XplevelsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('xplevels');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'manage' => array('admin', 'moderator', 'user')
+ );
+
+
+
+
+ /**
+ * Action: manage.
+ *
+ * Manage XP-levels.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function manage($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check permissions
+ if(
+ (is_null(self::$character) && count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0) &&
+ $seminary['created_user_id'] != self::$user['id']
+ ) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+
+ // Get XP-levels
+ $xplevels = $this->Xplevels->getXPLevelsForSeminary($seminary['id']);
+
+ // Values
+ $xplevelsValues = array();
+ foreach($xplevels as &$xplevel) {
+ $xplevelsValues[$xplevel['id']] = $xplevel;
+ }
+ $deletes = array();
+ $validations = array(
+ 'edit' => true,
+ 'create' => true
+ );
+
+ // Edit
+ $action = null;
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Edit and delete XP-levels
+ if(!is_null($this->request->getPostParam('edit')))
+ {
+ $action = 'edit';
+
+ // Get params
+ $xplevelsValues = $this->request->getPostParam('xplevels');
+ $deletes = $this->request->getPostParam('deletes');
+
+ // Validate params
+ if(!is_array($deletes)) {
+ $deletes = array();
+ }
+ foreach($xplevels as &$xplevel)
+ {
+ if(array_key_exists($xplevel['id'], $deletes)) {
+ continue;
+ }
+
+ $xplevelValidation = $this->Validation->validateParams($xplevelsValues[$xplevel['id']], array('xps'));
+ if($xplevelValidation !== true)
+ {
+ if(!is_array($validations['edit'])) {
+ $validations['edit'] = array();
+ }
+ if(!array_key_exists($xplevel['id'], $validations['edit']) || !is_array($validations['edit'][$xplevel['id']])) {
+ $validations['edit'][$xplevel['id']] = array();
+ }
+ $validations['edit'][$xplevel['id']] = $this->Validation->addValidationResults($validations['edit'][$xplevel['id']], 'xps', $xplevelValidation);
+ }
+ }
+
+ // Edit and delete
+ if($validations['edit'] === true)
+ {
+ $xplevels = array_reverse($xplevels);
+ foreach($xplevels as &$xplevel)
+ {
+ // Delete
+ if(array_key_exists($xplevel['id'], $deletes)) {
+ $this->Xplevels->deleteXPLevel($xplevel);
+ }
+ // Edit
+ elseif(array_key_exists($xplevel['id'], $xplevelsValues))
+ {
+ $this->Xplevels->editXPLevel(
+ $xplevel['id'],
+ $xplevelsValues[$xplevel['id']]['xps']
+ );
+ }
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(null, 3));
+ }
+ }
+
+ // Create XP-level
+ if(!is_null($this->request->getPostParam('create')))
+ {
+ $action = 'create';
+
+ // Get params and validate them
+ $xplevelnew = $this->request->getPostParam('xplevelnew');
+ $validations[$action] = $this->Validation->validateParams($xplevelnew, array('xps'));
+
+ // Create
+ if($validations[$action] === true)
+ {
+ $this->Xplevels->createXPLevel(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $xplevelnew['xps']
+ );
+
+ // Redirect
+ $this->redirect($this->linker->link(null, 3));
+ }
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array(
+ 'xps' => \nre\configs\AppConfig::$validation['xps']
+ );
+
+
+ // Set titile
+ $this->addTitleLocalized('Manage XP-levels');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('xplevels', $xplevels);
+ $this->set('xplevelsValues', $xplevelsValues);
+ $this->set('deletes', $deletes);
+ $this->set('action', $action);
+ $this->set('validations', $validations);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+ }
+
+?>
diff --git a/controllers/components/AchievementComponent.inc b/controllers/components/AchievementComponent.inc
new file mode 100644
index 00000000..f3ee9141
--- /dev/null
+++ b/controllers/components/AchievementComponent.inc
@@ -0,0 +1,205 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers\components;
+
+
+ /**
+ * Component to handle achievements.
+ *
+ * @author Oliver Hanraths
+ */
+ class AchievementComponent extends \nre\core\Component
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('achievements', 'characterroles');
+
+
+
+
+ /**
+ * Construct a new Achievements-component.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Check for newly achieved Achievements.
+ *
+ * @param int $seminaryId ID of Seminary to check Achievements for
+ * @param int $characterId ID of Character to check Achievements for
+ * @param array $checkConditions Conditions to check
+ * @return array List of newly achieved achievements
+ */
+ public function checkAchievements($seminaryId, $characterId, $checkConditions=null)
+ {
+ // Set conditions to check
+ if(!is_null($checkConditions) && !is_array($checkConditions)) {
+ $checkConditions = array($checkConditions);
+ }
+
+ // Get unachieved Achievments
+ $achievements = $this->Achievements->getUnachhievedAchievementsForCharacter($seminaryId, $characterId);
+ // Merge “only-once” Achievements
+ if(in_array('user', $this->Characterroles->getCharacterrolesForCharacterById($characterId))) {
+ $achievements = array_merge($achievements, $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary($seminaryId));
+ }
+
+ // Check conditions
+ $achievedAchievements = array();
+ foreach($achievements as &$achievement)
+ {
+ // Check condition to test
+ if(!is_null($checkConditions) && !in_array($achievement['condition'], $checkConditions)) {
+ continue;
+ }
+
+ // Check deadline
+ if(!is_null($achievement['deadline']) && $achievement['deadline'] < date('Y-m-d H:i:s')) {
+ continue;
+ }
+
+ // Get conditions
+ $conditions = array();
+ $progress = 0;
+ switch($achievement['condition'])
+ {
+ // Date conditions
+ case 'date':
+ $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']);
+ foreach($conditionsDate as &$condition)
+ {
+ $conditions[] = array(
+ 'func' => 'checkAchievementConditionDate',
+ 'params' => array(
+ $condition['select']
+ )
+ );
+ }
+ break;
+ // Character conditions
+ case 'character':
+ $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']);
+ foreach($conditionsCharacter as &$condition)
+ {
+ $conditions[] = array(
+ 'func' => 'checkAchievementConditionCharacter',
+ 'params' => array(
+ $condition['field'],
+ $condition['value'],
+ $characterId
+ )
+ );
+ }
+ break;
+ // Quest conditions
+ case 'quest':
+ $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']);
+ foreach($conditionsQuest as &$condition)
+ {
+ $conditions[] = array(
+ 'func' => 'checkAchievementConditionQuest',
+ 'params' => array(
+ $condition['field'],
+ $condition['count'],
+ $condition['value'],
+ $condition['status'],
+ $condition['groupby'],
+ $condition['quest_id'],
+ $characterId
+ )
+ );
+ }
+ break;
+ // Achievement conditions
+ case 'achievement':
+ $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']);
+ foreach($conditionsAchievement as &$condition)
+ {
+ $conditions[] = array(
+ 'func' => 'checkAchievementConditionAchievement',
+ 'params' => array(
+ $condition['field'],
+ $condition['count'],
+ $condition['value'],
+ $condition['groupby'],
+ $condition['meta_achievement_id'],
+ $characterId
+ )
+ );
+ }
+ break;
+ }
+
+ // Do not achieve Achievements without conditions
+ if(empty($conditions)) {
+ continue;
+ }
+
+ // Check conditions
+ $achieved = ($achievement['all_conditions'] == 1);
+ foreach($conditions as &$condition)
+ {
+ // Calculate result of condition
+ $result = call_user_func_array(
+ array(
+ $this->Achievements,
+ $condition['func']
+ ),
+ $condition['params']
+ );
+
+ // The overall result and abort if possible
+ if($achievement['all_conditions'])
+ {
+ if(!$result) {
+ $achieved = false;
+ break;
+ }
+ }
+ else
+ {
+ if($result) {
+ $achieved = true;
+ break;
+ }
+ }
+
+ }
+
+ // Achievement achieved
+ if($achieved)
+ {
+ // Set status
+ $this->Achievements->setAchievementAchieved($achievement['id'], $characterId);
+
+ // Add to list
+ $achievedAchievements[] = $achievement;
+ }
+ }
+
+
+ // Return newly achieved Achievements
+ return $achievedAchievements;
+ }
+
+ }
+
+?>
diff --git a/controllers/components/AuthComponent.inc b/controllers/components/AuthComponent.inc
new file mode 100644
index 00000000..eab74914
--- /dev/null
+++ b/controllers/components/AuthComponent.inc
@@ -0,0 +1,79 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers\components;
+
+
+ /**
+ * Component to handle authentication and authorization.
+ *
+ * @author Oliver Hanraths
+ */
+ class AuthComponent extends \nre\core\Component
+ {
+ /**
+ * Key to save a user-ID as
+ *
+ * @var string
+ */
+ const KEY_USER_ID = 'user_id';
+
+
+
+
+ /**
+ * Construct a new Auth-component.
+ */
+ public function __construct()
+ {
+ // Start session
+ if(session_id() === '') {
+ session_start();
+ }
+ }
+
+
+
+
+ /**
+ * Set the ID of the user that is currently logged in.
+ *
+ * @param int $userId ID of the currently logged in user
+ */
+ public function setUserId($userId)
+ {
+ if(is_null($userId)) {
+ unset($_SESSION[self::KEY_USER_ID]);
+ }
+ else {
+ $_SESSION[self::KEY_USER_ID] = $userId;
+ }
+ }
+
+
+ /**
+ * Get the ID of the user that is currently logged in.
+ *
+ * @return int ID of the currently logged in user
+ */
+ public function getUserId()
+ {
+ if(array_key_exists(self::KEY_USER_ID, $_SESSION)) {
+ return $_SESSION[self::KEY_USER_ID];
+ }
+
+
+ return null;
+ }
+
+ }
+
+?>
diff --git a/controllers/components/NotificationComponent.inc b/controllers/components/NotificationComponent.inc
new file mode 100644
index 00000000..ecf17ac1
--- /dev/null
+++ b/controllers/components/NotificationComponent.inc
@@ -0,0 +1,108 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers\components;
+
+
+ /**
+ * Component to handle user notifications
+ *
+ * @author Oliver Hanraths
+ */
+ class NotificationComponent extends \nre\core\Component
+ {
+ /**
+ * Type: Achievement
+ *
+ * @var string
+ */
+ const TYPE_ACHIEVEMENT = 'achievement';
+ /**
+ * Type: Level-up
+ *
+ * @var string
+ */
+ const TYPE_LEVELUP = 'levelup';
+ /**
+ * Key for Session-Array to store notifications in
+ *
+ * @var string
+ */
+ const SESSION_KEY = 'notifications';
+
+
+
+
+ /**
+ * Construct a new Notification-component.
+ */
+ public function __construct()
+ {
+ // Start session
+ if(session_id() === '') {
+ session_start();
+ }
+
+ // Prepare array
+ if(!array_key_exists(self::SESSION_KEY, $_SESSION)) {
+ $_SESSION[self::SESSION_KEY] = array();
+ }
+ }
+
+
+
+
+ /**
+ * Add a notification.
+ *
+ * @param string $type Type of notification
+ * @param string $message Message to display
+ * @param string $link Optional URL to link to
+ * @param string $image Optional URL of image to display
+ */
+ public function addNotification($type, $message, $link=null, $image=null)
+ {
+ $_SESSION[self::SESSION_KEY][] = array(
+ 'type' => $type,
+ 'message' => $message,
+ 'link' => $link,
+ 'image' => $image
+ );
+ }
+
+
+ /**
+ * Get all registered notifiactions and clear them.
+ *
+ * @return array List of existing notifications
+ */
+ public function getNotifications()
+ {
+ $notifications = $_SESSION[self::SESSION_KEY];
+ $this->clearNotifications();
+
+
+ return $notifications;
+ }
+
+
+ /**
+ * Clear all notifications currently registered
+ */
+ public function clearNotifications()
+ {
+ unset($_SESSION[self::SESSION_KEY]);
+ $_SESSION[self::SESSION_KEY] = array();
+ }
+
+ }
+
+?>
diff --git a/controllers/components/QuesttypedataComponent.inc b/controllers/components/QuesttypedataComponent.inc
new file mode 100644
index 00000000..1a55805f
--- /dev/null
+++ b/controllers/components/QuesttypedataComponent.inc
@@ -0,0 +1,89 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers\components;
+
+
+ /**
+ * Component to handle data for Questtypes between different Quest states.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypedataComponent extends \nre\core\Component
+ {
+ /**
+ * Session key
+ *
+ * @var string
+ */
+ const SESSION_KEY = 'questtypes';
+
+
+
+
+ /**
+ * Construct a new QuesttypesData-component.
+ */
+ public function __construct()
+ {
+ // Construct session place
+ if(!array_key_exists(self::SESSION_KEY, $_SESSION)) {
+ $_SESSION[self::SESSION_KEY] = array();
+ }
+ }
+
+
+
+
+ /**
+ * Set data with a key-value pair.
+ *
+ * @param int $questId ID of Quest to set data for
+ * @param mixed $key Key
+ * @param mixed $value Value
+ */
+ public function set($questId, $key, $value)
+ {
+ // Construct array
+ if(!array_key_exists($questId, $_SESSION[self::SESSION_KEY])) {
+ $_SESSION[self::SESSION_KEY][$questId] = array();
+ }
+
+ // Set data
+ $_SESSION[self::SESSION_KEY][$questId][$key] = $value;
+ }
+
+
+ /**
+ * Get data by a key.
+ *
+ * @param int $questId ID of Quest to set data for
+ * @param mixed $key Key
+ * @return mixed Value
+ */
+ public function get($questId, $key)
+ {
+ // Check array
+ if(!array_key_exists($questId, $_SESSION[self::SESSION_KEY])) {
+ return null;
+ }
+ if(!array_key_exists($key, $_SESSION[self::SESSION_KEY][$questId])) {
+ return null;
+ }
+
+
+ // Return data
+ return $_SESSION[self::SESSION_KEY][$questId][$key];
+ }
+
+ }
+
+?>
diff --git a/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc
new file mode 100644
index 00000000..0a639bf0
--- /dev/null
+++ b/controllers/components/ValidationComponent.inc
@@ -0,0 +1,183 @@
+
+ * @copyright 2014 Heinrich-Heine-Universität Düsseldorf
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link https://bitbucket.org/coderkun/the-legend-of-z
+ */
+
+ namespace hhu\z\controllers\components;
+
+
+ /**
+ * Component to validate user input.
+ *
+ * @author Oliver Hanraths
+ */
+ class ValidationComponent extends \nre\core\Component
+ {
+ /**
+ * Validation settings
+ *
+ * @var array
+ */
+ private $config;
+
+
+
+
+ /**
+ * Construct a new Validation-component.
+ */
+ public function __construct()
+ {
+ // Get validation settings from configuration
+ $this->config = \nre\configs\AppConfig::$validation;
+ }
+
+
+
+
+ /**
+ * Validate an user input.
+ *
+ * @param mixed $input User input to validate
+ * @param array $settings Validation setting
+ * @return mixed True or the settings the validation fails on
+ */
+ public function validate($input, $settings)
+ {
+ $validation = array();
+
+ // Min string length
+ if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) {
+ $validation['minlength'] = $settings['minlength'];
+ }
+ // Max string length
+ if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) {
+ $validation['maxlength'] = $settings['maxlength'];
+ }
+
+ // Regex
+ if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) {
+ $validation['regex'] = $settings['regex'];
+ }
+
+
+ // Return true or the failed fields
+ if(empty($validation)) {
+ return true;
+ }
+ return $validation;
+ }
+
+
+ /**
+ * Validate an user input parameter.
+ *
+ * @param array $params User input parameters
+ * @param array $index Names of parameter to validate and to validate against
+ * @return mixed True or the parameter with settings the validation failed on
+ */
+ public function validateParam($params, $index)
+ {
+ // Check parameter
+ if(!array_key_exists($index, $params)) {
+ throw new \nre\exceptions\ParamsNotValidException($index);
+ }
+ // Check settings
+ if(!array_key_exists($index, $this->config)) {
+ return true;
+ }
+
+
+ // Validate parameter and return result
+ return $this->validate($params[$index], $this->config[$index]);
+ }
+
+
+ /**
+ * Validate user input parameters.
+ *
+ * @param array $params User input parameters
+ * @param array $indices Names of parameters to validate and to validate against
+ * @return mixed True or the parameters with settings the validation failed on
+ */
+ public function validateParams($params, $indices)
+ {
+ // Validate parameters
+ $validation = true;
+ foreach($indices as $index) {
+ $validation = $this->addValidationResults($validation, $index, $this->validateParam($params, $index));
+ }
+
+
+ // Return validation results
+ return $validation;
+ }
+
+
+ /**
+ * Add a custom determined validation result to a validation
+ * array.
+ *
+ * @param mixed $validation Validation array to add result to
+ * @param string $index Name of parameter of the custom validation result
+ * @param string $setting Name of setting of the custom validation result
+ * @param mixed $result Validation result
+ * @return mixed The altered validation array
+ */
+ public function addValidationResult($validation, $index, $setting, $result)
+ {
+ // Create validation array
+ if(!is_array($validation)) {
+ $validation = array();
+ }
+
+ // Add validation results
+ if(!array_key_exists($index, $validation)) {
+ $validation[$index] = array();
+ }
+ $validation[$index][$setting] = $result;
+
+
+ // Return new validation result
+ return $validation;
+ }
+
+
+ /**
+ * Add custom determined validation results to a validation
+ * arary.
+ *
+ * @param mixed $validation Validation array to add result to
+ * @param string $index Name of parameter of the custom validation result
+ * @param mixed $result Validation result
+ * @return mixed The altered validation array
+ */
+ public function addValidationResults($validation, $index, $results)
+ {
+ // Create validation array
+ if(!is_array($validation)) {
+ $validation = array();
+ }
+
+ // Add validation results
+ if($results !== true) {
+ $validation[$index] = $results;
+ }
+
+
+ // Return new validation result
+ if(empty($validation)) {
+ return true;
+ }
+ return $validation;
+ }
+
+ }
+
+?>
diff --git a/core/Agent.inc b/core/Agent.inc
new file mode 100644
index 00000000..fb8fe8ac
--- /dev/null
+++ b/core/Agent.inc
@@ -0,0 +1,608 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Abstract class for the implementation af an Agent.
+ *
+ * @author coderkun
+ */
+ abstract class Agent
+ {
+ /**
+ * Name of BottomlevelAgent for showing inline errors
+ *
+ * @var string
+ */
+ const INLINEERROR_AGENT = 'inlineerror';
+
+ /**
+ * Current request
+ *
+ * @var Request
+ */
+ private $request;
+ /**
+ * Current response
+ *
+ * @var Response
+ */
+ private $response;
+ /**
+ * Log-system
+ *
+ * @var Logger
+ */
+ protected $log;
+ /**
+ * SubAgents
+ *
+ * @var array
+ */
+ protected $subAgents = array();
+ /**
+ * Controller of this Agent
+ *
+ * @var Controller
+ */
+ public $controller = null;
+
+
+
+
+ /**
+ * Load the class of an Agent.
+ *
+ * @static
+ * @throws \nre\exceptions\AgentNotFoundException
+ * @throws \nre\exceptions\AgentNotValidException
+ * @param string $agentName Name of the Agent to load
+ */
+ public static function load($agentName)
+ {
+ // Determine full classname
+ $agentType = self::getAgentType();
+ $className = self::getClassName($agentName, $agentType);
+
+ try {
+ // Load class
+ ClassLoader::load($className);
+
+ // Validate class
+ $parentAgentClassName = ClassLoader::concatClassNames($agentType, 'agent');
+ $parentAgentClassName = "\\nre\\agents\\$parentAgentClassName";
+ ClassLoader::check($className, $parentAgentClassName);
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \nre\exceptions\AgentNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \nre\exceptions\AgentNotFoundException($e->getClassName());
+ }
+ }
+
+
+
+ /**
+ * Instantiate an Agent (Factory Pattern).
+ *
+ * @static
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ * @throws \nre\exceptions\ControllerNotValidException
+ * @throws \nre\exceptions\ControllerNotFoundException
+ * @param string $agentName Name of the Agent to instantiate
+ * @param Request $request Current request
+ * @param Response $respone Current respone
+ * @param Logger $log Log-system
+ */
+ public static function factory($agentName, Request $request, Response $response, Logger $log=null)
+ {
+ // Determine full classname
+ $agentType = self::getAgentType();
+ $className = self::getClassName($agentName, $agentType);
+
+
+ // Construct and return Agent
+ return new $className($request, $response, $log);
+ }
+
+
+ /**
+ * Determine the type of an Agent.
+ *
+ * @static
+ * @return string Agent type
+ */
+ private static function getAgentType()
+ {
+ return strtolower(ClassLoader::getClassName(get_called_class()));
+ }
+
+
+ /**
+ * Determine the classname for the given Agent name.
+ *
+ * @static
+ * @param string $agentName Agent name to get classname of
+ * @param string $agentType Agent type of given Agent name
+ * @return string Classname for the Agent name
+ */
+ private static function getClassName($agentName, $agentType)
+ {
+ $className = ClassLoader::concatClassNames($agentName, 'agent');
+
+
+ return \nre\configs\AppConfig::$app['namespace']."agents\\$agentType\\$className";
+ }
+
+
+
+
+ /**
+ * Construct a new Agent.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ * @throws \nre\exceptions\ControllerNotValidException
+ * @throws \nre\exceptions\ControllerNotFoundException
+ * @param Request $request Current request
+ * @param Response $respone Current response
+ * @param Logger $log Log-system
+ */
+ protected function __construct(Request $request, Response $response, Logger $log=null)
+ {
+ // Store values
+ $this->request = $request;
+ $this->response = $response;
+ $this->log = $log;
+
+ // Construct SubAgent
+ $this->actionConstruct();
+
+ // Load corresponding Controller
+ $this->loadController();
+ }
+
+
+
+
+ /**
+ * Run the Controller of this Agent and its SubAgents.
+ *
+ * @throws \nre\exceptions\ParamsNotValidException
+ * @throws \nre\exceptions\IdNotFoundException
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\ActionNotFoundException
+ * @param Request $request Current request
+ * @param Response $response Current response
+ * @return \Exception Last occurred exception of SubAgents
+ */
+ public function run(Request $request, Response $response)
+ {
+ // Check Controller
+ if(!is_null($this->controller))
+ {
+ // Call prefilter
+ $this->controller->preFilter($request, $response);
+
+ // Run controller
+ $this->controller->run($request, $response);
+
+ // Call postfilter
+ $this->controller->postFilter($request, $response);
+ }
+
+
+ // Run SubAgents
+ $exception = null;
+ foreach($this->subAgents as &$subAgent)
+ {
+ try {
+ $subAgent['object']->run(
+ $request,
+ $subAgent['response']
+ );
+ }
+ catch(ParamsNotValidException $e) {
+ $subAgent = $this->errorInline($subAgent, $request, $e);
+ }
+ catch(IdNotFoundException $e) {
+ $subAgent = $this->errorInline($subAgent, $request, $e);
+ }
+ catch(DatamodelException $e) {
+ $exception = $e;
+ $subAgent = $this->errorInline($subAgent, $request, $e);
+ }
+ catch(ActionNotFoundException $e) {
+ $subAgent = $this->errorInline($subAgent, $request, $e);
+ }
+ }
+
+
+ // Return last occurred exception
+ return $exception;
+ }
+
+
+ /**
+ * Generate output of the Controller of this Agent and its
+ * SubAgents.
+ *
+ * @param array $data View data
+ * @return string Generated output
+ */
+ public function render($data=array())
+ {
+ // Check Controller
+ if(!is_null($this->controller))
+ {
+ // Render SubAgents
+ foreach($this->subAgents as $subAgent)
+ {
+ $label = array_key_exists('label', $subAgent) ? $subAgent['label'] : $subAgent['name'];
+ $data[$label] = $this->renderSubAgent($subAgent);
+ }
+
+ // Render the Controller of this agent
+ return $this->controller->render($data);
+ }
+ }
+
+
+
+
+ /**
+ * Construct SubAgents (per Action).
+ */
+ protected function actionConstruct()
+ {
+ // Action ermitteln
+ $action = $this->response->getParam(2);
+ if(is_null($action)) {
+ $action = $this->request->getParam(2, 'action');
+ $this->response->addParam($action);
+ }
+
+ // Initialisierungsmethode für diese Action ausführen
+ if(method_exists($this, $action))
+ {
+ call_user_func_array(
+ array(
+ $this,
+ $action
+ ),
+ array(
+ $this->request,
+ $this->response
+ )
+ );
+ }
+ }
+
+
+ /**
+ * Load the Controller of this Agent.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ * @throws \nre\exceptions\ControllerNotValidException
+ * @throws \nre\exceptions\ControllerNotFoundException
+ */
+ protected function loadController()
+ {
+ // Determine Controller name
+ $controllerName = ClassLoader::getClassName(get_class($this));
+
+ // Determine ToplevelAgent
+ $toplevelAgentName = $this->response->getParam(0);
+ if(is_null($toplevelAgentName)) {
+ $toplevelAgentName = $this->request->getParam(0, 'toplevel');
+ $this->response->addParam($toplevelAgentName);
+ }
+
+ // Determine Action
+ $action = $this->response->getParam(2);
+ if(is_null($action)) {
+ $action = $this->request->getParam(2, 'action');
+ $this->response->addParam($action);
+ }
+
+
+ // Load Controller
+ Controller::load($controllerName);
+
+ // Construct Controller
+ $this->controller = Controller::factory($controllerName, $toplevelAgentName, $action, $this);
+ }
+
+
+ /**
+ * Log an error.
+ *
+ * @param \Exception $exception Occurred exception
+ * @param int $logMode Log mode
+ */
+ protected function log($exception, $logMode)
+ {
+ if(is_null($this->log)) {
+ return;
+ }
+
+ $this->log->log(
+ $exception->getMessage(),
+ $logMode
+ );
+ }
+
+
+ /**
+ * Load a SubAgent and add it.
+ *
+ * @throws \nre\exceptions\ServiceUnavailableException
+ * @throws \nre\exceptions\AgentNotFoundException
+ * @throws \nre\exceptions\AgentNotValidException
+ * @param string $agentName Name of the Agent to load
+ * @param mixed … Additional parameters for the agent
+ */
+ protected function addSubAgent($agentName)
+ {
+ try {
+ call_user_func_array(
+ array(
+ $this,
+ '_addSubAgent'
+ ),
+ func_get_args()
+ );
+ }
+ catch(DatamodelException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ }
+
+
+ /**
+ * Load a SubAgent and add it.
+ *
+ * @throws \nre\exceptions\ServiceUnavailableException
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\AgentNotFoundException
+ * @throws \nre\exceptions\AgentNotValidException
+ * @param string $agentName Name of the Agent to load
+ * @param mixed … Additional parameters for the agent
+ */
+ protected function _addSubAgent($agentName)
+ {
+ try {
+ // Load Agent
+ \nre\agents\BottomlevelAgent::load($agentName);
+
+ // Construct Agent
+ $this->subAgents[] = call_user_func_array(
+ array(
+ $this,
+ 'newSubAgent'
+ ),
+ func_get_args()
+ );
+ }
+ catch(ViewNotFoundException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(DriverNotValidException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(DriverNotFoundException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(ModelNotValidException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(ModelNotFoundException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(ControllerNotValidException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(ControllerNotFoundException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(AgentNotValidException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ catch(AgentNotFoundException $e) {
+ $this->subAgents[] = $this->newInlineError($agentName, $e);
+ }
+ }
+
+
+
+
+ /**
+ * Create a new SubAgent.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @param string $agentName Agent name
+ * @return array SubAgent
+ */
+ private function newSubAgent($agentName)
+ {
+ // Response
+ $response = clone $this->response;
+ $response->clearParams(1);
+ $params = func_get_args();
+ if(count($params) < 2 || empty($params[1])) {
+ $params[1] = \nre\configs\CoreConfig::$defaults['action'];
+ }
+ call_user_func_array(
+ array(
+ $response,
+ 'addParams'
+ ),
+ $params
+ );
+
+ return array(
+ 'name' => strtolower($agentName),
+ 'response' => $response,
+ 'object' => \nre\agents\BottomlevelAgent::factory(
+ $agentName,
+ $this->request,
+ $response,
+ $this->log
+ )
+ );
+ }
+
+
+ /**
+ * Render a SubAgent.
+ *
+ * @param array $subAgent SubAgent to render
+ * @return string Generated output
+ */
+ private function renderSubAgent(&$subAgent)
+ {
+ // Check for InlineError
+ if(array_key_exists('inlineerror', $subAgent) && !empty($subAgent['inlineerror'])) {
+ return file_get_contents($subAgent['inlineerror']);
+ }
+
+
+ // Rendern SubAgent and return its output
+ return $subAgent['object']->render();
+ }
+
+
+ /**
+ * Handle the exception of a SubAgent.
+ *
+ * @param array $subAgent Original (Sub-) Agent
+ * @param Request $request Current request
+ * @param Excepiton $exception Occurred exception
+ * @return array InlineError-SubAgent
+ */
+ private function errorInline($subAgent, $request, $exception)
+ {
+ // Create the SubAgent for the exception
+ $subAgent = $this->newInlineError($subAgent['name'], $exception);
+
+
+ // Run the InlineError-SubAgent
+ try {
+ $subAgent['object']->run(
+ $request,
+ $subAgent['response']
+ );
+ }
+ catch(ActionNotFoundException $e) {
+ $this->log($e, Logger::LOGMODE_AUTO);
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+
+
+ // Return the InlineError-SubAgent
+ return $subAgent;
+ }
+
+
+ /**
+ * Create a new InlineError.
+ *
+ * @param string $label Name of the original Agent
+ * @param \Exception $exception Occurred exception
+ */
+ private function newInlineError($label, $exception)
+ {
+ // Log error
+ $this->log($exception, Logger::LOGMODE_AUTO);
+
+ // Determine Agent name
+ $agentName = self::INLINEERROR_AGENT;
+
+ // Create SugAgent
+ $subAgent = array();
+
+
+ try {
+ // Load Agenten
+ \nre\agents\BottomlevelAgent::load($agentName);
+
+ // Construct Agent
+ $subAgent = $this->newSubAgent($agentName);
+ $subAgent['label'] = $label;
+ $subAgent['response']->addParam($exception);
+ }
+ catch(ViewNotFoundException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(DatamodelException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(DriverNotValidException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(DriverNotFoundException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(ModelNotValidException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(ModelNotFoundException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(ControllerNotValidException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(ControllerNotFoundException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(AgentNotValidException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+ catch(AgentNotFoundException $e) {
+ $subAgent['inlineerror'] = $this->newInlineErrorService();
+ }
+
+
+ // Return SubAgent
+ return $subAgent;
+ }
+
+
+ /**
+ * Handle a hardcore error that could not be handled by the
+ * system.
+ */
+ private function newInlineErrorService()
+ {
+ // Read and return static error file
+ return ROOT.DS.\nre\configs\CoreConfig::getClassDir('views').DS.\nre\configs\CoreConfig::$defaults['inlineErrorFile'].\nre\configs\Config::getFileExt('views');
+ }
+
+ }
+
+?>
diff --git a/core/Api.inc b/core/Api.inc
new file mode 100644
index 00000000..9cd1cfdc
--- /dev/null
+++ b/core/Api.inc
@@ -0,0 +1,163 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Abstract class to implement an API.
+ *
+ * The API is the center of each application and specifies how and what
+ * to run and render.
+ *
+ * @author coderkun
+ */
+ abstract class Api
+ {
+ /**
+ * Die aktuelle Anfrage
+ *
+ * @var Request
+ */
+ protected $request;
+ /**
+ * Der Toplevelagent
+ *
+ * @var \nre\agents\ToplevelAgent
+ */
+ private $toplevelAgent = null;
+ /**
+ * Die aktuelle Antwort
+ *
+ * @var Response
+ */
+ protected $response;
+ /**
+ * Log-System
+ *
+ * @var Logger
+ */
+ protected $log;
+
+
+
+
+ /**
+ * Construct a new API.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function __construct(Request $request, Response $response)
+ {
+ // Store request
+ $this->request = $request;
+
+ // Store response
+ $this->response = $response;
+
+ // Init logging
+ $this->log = new \nre\core\Logger();
+ }
+
+
+
+
+ /**
+ * Run the application.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ * @throws \nre\exceptions\ControllerNotValidException
+ * @throws \nre\exceptions\ControllerNotFoundException
+ * @throws \nre\exceptions\AgentNotValidException
+ * @throws \nre\exceptions\AgentNotFoundException
+ * @return \Exception Last occurred exception of an subagent
+ */
+ public function run()
+ {
+ // Load ToplevelAgent
+ $this->loadToplevelAgent();
+
+ // Run ToplevelAgent
+ return $this->toplevelAgent->run($this->request, $this->response);
+ }
+
+
+ /**
+ * Render the output.
+ */
+ public function render()
+ {
+ // Check exit-status
+ if($this->response->getExit()) {
+ return;
+ }
+
+ // Render ToplevelAgent
+ $this->response->setOutput($this->toplevelAgent->render());
+ }
+
+
+
+
+ /**
+ * Log an exception
+ *
+ * @param \Exception $exception Occurred exception
+ * @param int $logMode Log-mode
+ */
+ protected function log($exception, $logMode)
+ {
+ $this->log->log(
+ $exception->getMessage(),
+ $logMode
+ );
+ }
+
+
+
+
+ /**
+ * Load the ToplevelAgent specified by the request.
+ *
+ * @throws \nre\exceptions\ServiceUnavailableException
+ * @throws \nre\exceptions\AgentNotValidException
+ * @throws \nre\exceptions\AgentNotFoundException
+ */
+ private function loadToplevelAgent()
+ {
+ // Determine agent
+ $agentName = $this->response->getParam(0);
+ if(is_null($agentName)) {
+ $agentName = $this->request->getParam(0, 'toplevel');
+ $this->response->addParam($agentName);
+ }
+
+ // Load agent
+ \nre\agents\ToplevelAgent::load($agentName);
+
+ // Construct agent
+ $this->toplevelAgent = \nre\agents\ToplevelAgent::factory(
+ $agentName,
+ $this->request,
+ $this->response,
+ $this->log
+ );
+ }
+
+ }
+
+?>
diff --git a/core/Autoloader.inc b/core/Autoloader.inc
new file mode 100644
index 00000000..fdbf73bc
--- /dev/null
+++ b/core/Autoloader.inc
@@ -0,0 +1,98 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Autoloader.
+ *
+ * This class tries to load not yet used classes.
+ *
+ * @author coderkun
+ */
+ class Autoloader
+ {
+ /**
+ * Private construct().
+ */
+ private function __construct() {}
+
+ /**
+ * Private clone().
+ */
+ private function __clone() {}
+
+
+
+
+ /**
+ * Register load-method.
+ */
+ public static function register()
+ {
+ spl_autoload_register(
+ array(
+ get_class(),
+ 'load'
+ )
+ );
+ }
+
+
+ /**
+ * Look for the given class and try to load it.
+ *
+ * @param string $fullClassName Name of class to load
+ */
+ public static function load($fullClassName)
+ {
+ $fullClassNameA = explode('\\', $fullClassName);
+
+ if(strpos($fullClassName, \nre\configs\CoreConfig::$core['namespace']) !== 0)
+ {
+ // App
+ $className = array_slice($fullClassNameA, substr_count(\nre\configs\AppConfig::$app['namespace'], '\\'));
+ array_unshift($className, \nre\configs\CoreConfig::getClassDir('app'));
+ $filename = ROOT.DS.implode(DS, $className).\nre\configs\CoreConfig::getFileExt('includes');
+ if(file_exists($filename)) {
+ require_once($filename);
+ }
+ }
+ else
+ {
+ // Core
+ $className = array_slice($fullClassNameA, substr_count(\nre\configs\CoreConfig::$core['namespace'], '\\'));
+ $filename = ROOT.DS.implode(DS, $className).\nre\configs\CoreConfig::getFileExt('includes');
+ if(file_exists($filename)) {
+ require_once($filename);
+ }
+ }
+
+
+ }
+
+
+ /**
+ * Determine classtype of a class.
+ *
+ * @param string $className Name of the class to determine the classtype of
+ * @return string Classtype of the given class
+ */
+ public static function getClassType($className)
+ {
+ // CamelCase
+ return strtolower(preg_replace('/^.*([A-Z][^A-Z]+)$/', '$1', $className));
+ }
+
+ }
+
+?>
diff --git a/core/ClassLoader.inc b/core/ClassLoader.inc
new file mode 100644
index 00000000..e999568a
--- /dev/null
+++ b/core/ClassLoader.inc
@@ -0,0 +1,129 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Class for safely loading classes.
+ *
+ * @author coderkun
+ */
+ class ClassLoader
+ {
+
+
+
+
+ /**
+ * Load a class.
+ *
+ * @throws \nre\exceptions\ClassNotFoundException
+ * @param string $className Name of the class to load
+ */
+ public static function load($fullClassName)
+ {
+ // Determine folder to look in
+ $className = explode('\\', $fullClassName);
+ $className = array_slice($className, substr_count(\nre\configs\AppConfig::$app['namespace'], '\\'));
+
+ // Determine filename
+ $fileName = ROOT.DS.implode(DS, $className). \nre\configs\CoreConfig::getFileExt('includes');
+
+
+ // Check file
+ if(!file_exists($fileName))
+ {
+ throw new \nre\exceptions\ClassNotFoundException(
+ $fullClassName
+ );
+ }
+
+ // Include file
+ include_once($fileName);
+ }
+
+
+ /**
+ * Check inheritance of a class.
+ *
+ * @throws \nre\exceptions\ClassNotValidException
+ * @param string $className Name of the class to check
+ * @param string $parentClassName Name of the parent class
+ */
+ public static function check($className, $parentClassName)
+ {
+ // Check if class is subclass of parent class
+ if(!is_subclass_of($className, $parentClassName)) {
+ throw new \nre\exceptions\ClassNotValidException(
+ $className
+ );
+ }
+ }
+
+
+ /**
+ * Strip the namespace from a class name.
+ *
+ * @param string $class Name of a class including its namespace
+ * @return string Name of the given class without its namespace
+ */
+ public static function stripNamespace($class)
+ {
+ return array_slice(explode('\\', $class), -1)[0];
+ }
+
+
+ /**
+ * Strip the class type from a class name.
+ *
+ * @param string $className Name of a class
+ * @return string Name of the given class without its class type
+ */
+ public static function stripClassType($className)
+ {
+ return preg_replace('/^(.*)[A-Z][^A-Z]+$/', '$1', $className);
+ }
+
+
+ /**
+ * Strip the namespace and the class type of a full class name
+ * to get only its name.
+ *
+ * @param string $class Full name of a class
+ * @return string Only the name of the given class
+ */
+ public static function getClassName($class)
+ {
+ return self::stripClassType(self::stripNamespace($class));
+ }
+
+
+ /**
+ * Concatenate strings to a class name following the CamelCase
+ * pattern.
+ *
+ * @param string $className1 Arbitrary number of strings to concat
+ * @return string Class name as CamelCase
+ */
+ public static function concatClassNames($className1)
+ {
+ return implode('', array_map(
+ function($arg) {
+ return ucfirst(strtolower($arg));
+ },
+ func_get_args()
+ ));
+ }
+
+ }
+
+?>
diff --git a/core/Component.inc b/core/Component.inc
new file mode 100644
index 00000000..2cc03cdd
--- /dev/null
+++ b/core/Component.inc
@@ -0,0 +1,159 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Abstract class to implement a (Controller) Component.
+ *
+ * @author coderkun
+ */
+ abstract class Component
+ {
+
+
+
+
+ /**
+ * Load the class of a Component.
+ *
+ * @throws \nre\exceptions\ComponentNotFoundException
+ * @throws \nre\exceptions\ComponentNotValidException
+ * @param string $componentName Name of the Component to load
+ */
+ public static function load($componentName)
+ {
+ // Determine full classname
+ $className = self::getClassName($componentName);
+
+ try {
+ // Load class
+ ClassLoader::load($className);
+
+ // Validate class
+ ClassLoader::check($className, get_class());
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \nre\exceptions\ComponentNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \nre\exceptions\ComponentNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a Component (Factory Pattern).
+ *
+ * @param string $componentName Name of the Component to instantiate
+ */
+ public static function factory($componentName)
+ {
+ // Determine full classname
+ $className = self::getClassName($componentName);
+
+ // Construct and return Controller
+ return new $className();
+ }
+
+
+ /**
+ * Determine the classname for the given Component name.
+ *
+ * @param string $componentName Component name to get classname of
+ * @return string Classname for the Component name
+ */
+ private static function getClassName($componentName)
+ {
+ $className = \nre\core\ClassLoader::concatClassNames($componentName, \nre\core\ClassLoader::stripNamespace(get_class()));
+
+
+ return \nre\configs\AppConfig::$app['namespace']."controllers\\components\\$className";
+ }
+
+
+
+
+ /**
+ * Construct a new (Controller) Component.
+ *
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ */
+ protected function __construct()
+ {
+ // Load Models
+ $this->loadModels();
+ }
+
+
+ /**
+ * Load the Models of this Component.
+ *
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ */
+ protected function loadModels()
+ {
+ // Determine Models
+ $explicit = false;
+ $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this)));
+ if(property_exists($this, 'models'))
+ {
+ $models = $this->models;
+ $explicit = true;
+ }
+ if(!is_array($models)) {
+ $models = array($models);
+ }
+ // Models of parent classes
+ $parent = $this;
+ while($parent = get_parent_class($parent))
+ {
+ $properties = get_class_vars($parent);
+ if(array_key_exists('models', $properties)) {
+ $models = array_merge($models, $properties['models']);
+ }
+ }
+ $models = array_unique($models);
+
+ // Load Models
+ foreach($models as &$model)
+ {
+ try {
+ // Load class
+ Model::load($model);
+
+ // Construct Model
+ $modelName = ucfirst(strtolower($model));
+ $this->$modelName = Model::factory($model);
+ }
+ catch(\nre\exceptions\ModelNotValidException $e) {
+ if($explicit) {
+ throw $e;
+ }
+ }
+ catch(\nre\exceptions\ModelNotFoundException $e) {
+ if($explicit) {
+ throw $e;
+ }
+ }
+ }
+ }
+
+ }
+
+?>
diff --git a/core/Config.inc b/core/Config.inc
new file mode 100644
index 00000000..adf858cd
--- /dev/null
+++ b/core/Config.inc
@@ -0,0 +1,49 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Configuration.
+ *
+ * This class does not hold any configuration value but helps to
+ * determine values that can be hold by AppConfig or CoreConfig.
+ *
+ * @author coderkun
+ */
+ final class Config
+ {
+
+
+
+
+ /**
+ * Get a default value.
+ *
+ * @param string $index Index of value to get
+ */
+ public static function getDefault($index)
+ {
+ if(array_key_exists($index, \nre\configs\AppConfig::$defaults)) {
+ return \nre\configs\AppConfig::$defaults[$index];
+ }
+ if(array_key_exists($index, \nre\configs\CoreConfig::$defaults)) {
+ return \nre\configs\CoreConfig::$defaults[$index];
+ }
+
+
+ return null;
+ }
+
+ }
+
+?>
diff --git a/core/Controller.inc b/core/Controller.inc
new file mode 100644
index 00000000..fd74a79f
--- /dev/null
+++ b/core/Controller.inc
@@ -0,0 +1,431 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Abstract class for implementing a Controller.
+ *
+ * @author coderkun
+ */
+ abstract class Controller
+ {
+ /**
+ * Corresponding Agent
+ *
+ * @var Agent
+ */
+ protected $agent;
+ /**
+ * View of the Controller
+ *
+ * @var View
+ */
+ protected $view = null;
+ /**
+ * Data to pass to the View
+ *
+ * @var array
+ */
+ protected $viewData = array();
+ /**
+ * Current request
+ *
+ * @var Request
+ */
+ protected $request = null;
+ /**
+ * Current response
+ *
+ * @var Response
+ */
+ protected $response = null;
+
+
+
+
+ /**
+ * Load the class of a Controller.
+ *
+ * @throws \nre\exceptions\ControllerNotFoundException
+ * @throws \nre\exceptions\ControllerNotValidException
+ * @param string $controllerName Name of the Controller to load
+ */
+ public static function load($controllerName)
+ {
+ // Determine full classname
+ $className = self::getClassName($controllerName);
+
+ try {
+ // Load class
+ ClassLoader::load($className);
+
+ // Validate class
+ ClassLoader::check($className, get_class());
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \nre\exceptions\ControllerNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \nre\exceptions\ControllerNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a Controller (Factory Pattern).
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @param string $controllerName Name of the Controller to instantiate
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ * @param string $agent Corresponding Agent
+ */
+ public static function factory($controllerName, $layoutName, $action, $agent)
+ {
+ // Determine full classname
+ $className = self::getClassName($controllerName);
+
+ // Construct and return Controller
+ return new $className($layoutName, $action, $agent);
+ }
+
+
+ /**
+ * Determine the classname for the given Controller name.
+ *
+ * @param string $controllerName Controller name to get classname of
+ * @return string Classname for the Controller name
+ */
+ private static function getClassName($controllerName)
+ {
+ $className = \nre\core\ClassLoader::concatClassNames($controllerName, \nre\core\ClassLoader::stripNamespace(get_class()));
+
+
+ return \nre\configs\AppConfig::$app['namespace']."controllers\\$className";
+ }
+
+
+
+
+ /**
+ * Construct a new Controller.
+ *
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ * @param Agent $agent Corresponding Agent
+ */
+ protected function __construct($layoutName, $action, $agent)
+ {
+ // Store values
+ $this->agent = $agent;
+
+ // Load Components
+ $this->loadComponents();
+
+ // Load Models
+ $this->loadModels();
+
+ // Load View
+ $this->loadView($layoutName, $action);
+ }
+
+
+
+
+ /**
+ * Prefilter that is executed before running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function preFilter(Request $request, Response $response)
+ {
+ // Request speichern
+ $this->request = $request;
+
+ // Response speichern
+ $this->response = $response;
+
+ }
+
+
+ /**
+ * Prefilter that is executed after running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function postFilter(Request $request, Response $response)
+ {
+ }
+
+
+ /**
+ * Run the Controller.
+ *
+ * This method executes the Action of the Controller defined by
+ * the current Request.
+ *
+ * @throws \nre\exceptions\ParamsNotValidException
+ * @throws \nre\exceptions\IdNotFoundException
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\ActionNotFoundException
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function run(Request $request, Response $response)
+ {
+ // Determine Action
+ $action = $response->getParam(2, 'action');
+ if(!method_exists($this, $action)) {
+ throw new \nre\exceptions\ActionNotFoundException($action);
+ }
+
+ // Determine parameters
+ $params = $response->getParams(3);
+ if(empty($params)) {
+ $params = $request->getParams(3);
+ }
+
+ // Fill missing parameters
+ $rc = new \ReflectionClass($this);
+ $nullParamsCount = $rc->getMethod($action)->getNumberOfParameters() - count($params);
+ $nullParams = ($nullParamsCount > 0 ? array_fill(0, $nullParamsCount, NULL) : array());
+
+
+ // Call Action
+ call_user_func_array(
+ array(
+ $this,
+ $action
+ ),
+ array_merge(
+ $params,
+ $nullParams
+ )
+ );
+ }
+
+
+ /**
+ * Generate the output.
+ *
+ * @param array $viewData Data to pass to the View
+ * @return string Generated output
+ */
+ public function render($viewData=null)
+ {
+ // Combine given data and data of this Controller
+ $data = $this->viewData;
+ if(!is_null($viewData)) {
+ $data = array_merge($viewData, $data);
+ }
+
+ // Rendern and return output
+ return $this->view->render($data);
+ }
+
+
+
+
+ /**
+ * Set data for the View.
+ *
+ * @param string $name Key
+ * @param mixed $data Value
+ */
+ protected function set($name, $data)
+ {
+ $this->viewData[$name] = $data;
+ }
+
+
+ /**
+ * Redirect to the given URL.
+ *
+ * @param string $url Relative URL
+ */
+ protected function redirect($url)
+ {
+ $url = 'http://'.$_SERVER['HTTP_HOST'].$url;
+ header('Location: '.$url);
+ exit;
+ }
+
+
+ /**
+ * Check if Models of this Controller are loaded and available.
+ *
+ * @param string $modelName Arbitrary number of Models to check
+ * @return bool All given Models are loaded and available
+ */
+ protected function checkModels($modelName)
+ {
+ foreach(func_get_args() as $modelName)
+ {
+ if(!isset($this->$modelName) || !is_subclass_of($this->$modelName, 'Model')) {
+ return false;
+ }
+ }
+
+
+ return true;
+ }
+
+
+ /**
+ * Get the View of the Controller
+ *
+ * @return View View of the Controller
+ */
+ protected function getView()
+ {
+ return $this->view;
+ }
+
+
+
+
+ /**
+ * Load the Components of this Controller.
+ *
+ * @throws \nre\exceptions\ComponentNotValidException
+ * @throws \nre\exceptions\ComponentNotFoundException
+ */
+ private function loadComponents()
+ {
+ // Determine components
+ $components = array();
+ if(property_exists($this, 'components')) {
+ $components = $this->components;
+ }
+ if(!is_array($components)) {
+ $components = array($components);
+ }
+ // Components of parent classes
+ $parent = $this;
+ while($parent = get_parent_class($parent))
+ {
+ $properties = get_class_vars($parent);
+ if(array_key_exists('components', $properties)) {
+ $components = array_merge($components, $properties['components']);
+ }
+ }
+
+ // Load components
+ foreach($components as &$component)
+ {
+ // Load class
+ Component::load($component);
+
+ // Construct component
+ $componentName = ucfirst(strtolower($component));
+ $this->$componentName = Component::factory($component);
+ }
+ }
+
+
+ /**
+ * Load the Models of this Controller.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ */
+ protected function loadModels()
+ {
+ // Determine Models
+ $explicit = false;
+ $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this)));
+ if(property_exists($this, 'models'))
+ {
+ $models = $this->models;
+ $explicit = true;
+ }
+ if(!is_array($models)) {
+ $models = array($models);
+ }
+ // Models of parent classes
+ $parent = $this;
+ while($parent = get_parent_class($parent))
+ {
+ $properties = get_class_vars($parent);
+ if(array_key_exists('models', $properties)) {
+ $models = array_merge($models, $properties['models']);
+ }
+ }
+ $models = array_unique($models);
+
+ // Load Models
+ foreach($models as &$model)
+ {
+ try {
+ // Load class
+ Model::load($model);
+
+ // Construct Model
+ $modelName = ucfirst(strtolower($model));
+ $this->$modelName = Model::factory($model);
+ }
+ catch(\nre\exceptions\ModelNotValidException $e) {
+ if($explicit) {
+ throw $e;
+ }
+ }
+ catch(\nre\exceptions\ModelNotFoundException $e) {
+ if($explicit) {
+ throw $e;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Load the View of this Controller.
+ *
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ */
+ protected function loadView($layoutName, $action)
+ {
+ // Check Layout name
+ if(is_null($layoutName)) {
+ return;
+ }
+
+ // Determine controller name
+ $controllerName = \nre\core\ClassLoader::getClassName(get_class($this));
+
+
+ // Load view
+ $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent');
+ $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel);
+ }
+
+ }
+
+?>
diff --git a/core/Driver.inc b/core/Driver.inc
new file mode 100644
index 00000000..ac2fa050
--- /dev/null
+++ b/core/Driver.inc
@@ -0,0 +1,97 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Abstract class for implementing a Driver.
+ *
+ * @author coderkun
+ */
+ abstract class Driver
+ {
+
+
+
+
+ /**
+ * Load the class of a Driver.
+ *
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @param string $driverName Name of the Driver to load
+ */
+ public static function load($driverName)
+ {
+ // Determine full classname
+ $className = self::getClassName($driverName);
+
+ try {
+ // Load class
+ ClassLoader::load($className);
+
+ // Validate class
+ ClassLoader::check($className, get_class());
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \nre\exceptions\DriverNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \nre\exceptions\DriverNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a Driver (Factory Pattern).
+ *
+ * @param string $driverName Name of the Driver to instantiate
+ * @param array $config Configuration settings
+ */
+ public static function factory($driverName, $config)
+ {
+ // Determine full classname
+ $className = self::getClassName($driverName);
+
+
+ // Construct and return Driver
+ return $className::singleton($config);
+ }
+
+
+ /**
+ * Determine the classname for the given Driver name.
+ *
+ * @param string $driverName Driver name to get classname of
+ * @return string Classname fore the Driver name
+ */
+ private static function getClassName($driverName)
+ {
+ $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class()));
+
+
+ return "\\nre\\drivers\\$className";
+ }
+
+
+
+
+ /**
+ * Construct a new Driver.
+ */
+ protected function __construct()
+ {
+ }
+
+ }
+
+?>
diff --git a/core/Exception.inc b/core/Exception.inc
new file mode 100644
index 00000000..21ed842b
--- /dev/null
+++ b/core/Exception.inc
@@ -0,0 +1,65 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Exception class.
+ *
+ * @author coderkun
+ */
+ class Exception extends \Exception
+ {
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $message Error message
+ * @param int $code Error code
+ * @param string $name Name to insert
+ */
+ function __construct($message, $code, $name=null)
+ {
+ parent::__construct(
+ $this->concat(
+ $message,
+ $name
+ ),
+ $code
+ );
+ }
+
+
+
+
+ /**
+ * Insert the name in a message
+ *
+ * @param string $message Error message
+ * @param string $name Name to insert
+ */
+ private function concat($message, $name)
+ {
+ if(is_null($name)) {
+ return $message;
+ }
+
+
+ return "$message: $name";
+ }
+
+ }
+
+?>
diff --git a/core/Linker.inc b/core/Linker.inc
new file mode 100644
index 00000000..68c1902f
--- /dev/null
+++ b/core/Linker.inc
@@ -0,0 +1,313 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Class to create web links based on the current request.
+ *
+ * @author coderkun
+ */
+ class Linker
+ {
+ /**
+ * Current request
+ *
+ * @var Request
+ */
+ private $request;
+
+
+
+
+ /**
+ * Construct a new linker.
+ *
+ * @param Request $request Current request
+ */
+ function __construct(\nre\requests\WebRequest $request)
+ {
+ $this->request = $request;
+ }
+
+
+
+
+ /**
+ * Mask parameters to be used in an URL.
+ *
+ * @param string $param1 First parameter
+ * @return string Masked parameters as string
+ */
+ public static function createLinkParam($param1)
+ {
+ return implode(
+ \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'],
+ call_user_func_array(
+ '\nre\core\Linker::createLinkParams',
+ func_get_args()
+ )
+ );
+ }
+
+
+ /**
+ * Mask parameters to be used in an URL.
+ *
+ * @param string $param1 First parameter
+ * @return string Masked parameters as array
+ */
+ public static function createLinkParams($param1)
+ {
+ // Parameters
+ $linkParams = array();
+ $params = func_get_args();
+
+ foreach($params as $param)
+ {
+ // Delete critical signs
+ $specials = array('/', '?', '&', '#');
+ foreach($specials as &$special) {
+ $param = str_replace($special, '', $param);
+ }
+
+ // Process parameter
+ $param = str_replace(
+ ' ',
+ \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'],
+ substr(
+ $param,
+ 0,
+ \nre\configs\CoreConfig::$classes['linker']['url']['length']
+ )
+ );
+
+ // Encode parameter
+ $linkParams[] = $param;
+ }
+
+
+ // Return link parameters
+ return $linkParams;
+ }
+
+
+
+
+ /**
+ * Create a web link.
+ *
+ * @param array $params Parameters to use
+ * @param int $offset Ignore first parameters
+ * @param bool $exclusiveParams Use only the given parameters
+ * @param array $getParams GET-parameter to use
+ * @param bool $exclusiveGetParams Use only the given GET-parameters
+ * @param string $anchor Target anchor
+ * @param bool $absolute Include hostname etc. for an absolute URL
+ * @return string Created link
+ */
+ public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false)
+ {
+ // Make current request to response
+ $response = new \nre\responses\WebResponse();
+
+
+ // Check parameters
+ if(is_null($params)) {
+ $params = array();
+ }
+ elseif(!is_array($params)) {
+ $params = array($params);
+ }
+
+ // Set parameters from request
+ $reqParams = array_slice($this->request->getParams(), 1, $offset);
+ if(count($reqParams) < $offset && $offset > 0) {
+ $reqParams[] = $this->request->getParam(1, 'intermediate');
+ }
+ if(count($reqParams) < $offset && $offset > 1) {
+ $reqParams[] = $this->request->getParam(2, 'action');
+ }
+ $params = array_merge($reqParams, $params);
+
+ // Use Layout
+ $layout = \nre\configs\AppConfig::$defaults['toplevel'];
+ if(!empty($getParams) && array_key_exists('layout', $getParams)) {
+ $layout = $getParams['layout'];
+ }
+ array_unshift($params, $layout);
+
+ // Use parameters from request only
+ if(!$exclusiveParams)
+ {
+ $params = array_merge(
+ $params,
+ array_slice(
+ $this->request->getParams(),
+ count($params)
+ )
+ );
+ }
+
+ // Encode parameters
+ $params = array_map('rawurlencode', $params);
+
+ // Set parameters
+ call_user_func_array(
+ array(
+ $response,
+ 'addParams'
+ ),
+ $params
+ );
+
+
+ // Check GET-parameters
+ if(is_null($getParams)) {
+ $getParams = array();
+ }
+ elseif(!is_array($params)) {
+ $params = array($params);
+ }
+ if(!$exclusiveGetParams)
+ {
+ $getParams = array_merge(
+ $this->request->getGetParams(),
+ $getParams
+ );
+ }
+
+ // Set GET-parameters
+ $response->addGetParams($getParams);
+
+
+ // Create and return link
+ return self::createLink($this->request, $response, $anchor, $absolute);
+ }
+
+
+ /**
+ * Create a link from an URL.
+ *
+ * @param string $url URL to create link from
+ * @param bool $absolute Create absolute URL
+ * @return string Created link
+ */
+ public function hardlink($url, $absolute=false)
+ {
+ return $this->createUrl($url, $this->request, $absolute);
+ }
+
+
+
+
+ /**
+ * Create a link.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ * @param string $anchor Anchor on target
+ * @param bool $absolute Create absolute link
+ * @return string Created link
+ */
+ private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false)
+ {
+ // Check response
+ if(is_null($response)) {
+ return null;
+ }
+
+
+ // Get parameters
+ $params = $response->getParams(1);
+
+ // Check Action
+ if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) {
+ array_pop($params);
+ }
+
+ // Set parameters
+ $link = implode('/', $params);
+
+ // Apply reverse-routes
+ $link = $request->applyReverseRoutes($link);
+
+
+ // Get GET-parameters
+ $getParams = $response->getGetParams();
+
+ // Layout überprüfen
+ if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) {
+ unset($getParams['layout']);
+ }
+
+ // Set GET-parameters
+ if(array_key_exists('url', $getParams)) {
+ unset($getParams['url']);
+ }
+ if(count($getParams) > 0) {
+ $link .= '?'.http_build_query($getParams);
+ }
+
+ // Add anchor
+ if(!is_null($anchor)) {
+ $link .= "#$anchor";
+ }
+
+
+ // Create URL
+ $url = self::createUrl($link, $request, $absolute);
+
+
+ return $url;
+ }
+
+
+ /**
+ * Adapt a link to the environment.
+ *
+ * @param string $url URL
+ * @param Request $request Current request
+ * @param bool $absolute Create absolute URL
+ * @return string Adapted URL
+ */
+ private static function createUrl($url, Request $request, $absolute=false)
+ {
+ // Variables
+ $server = $_SERVER['SERVER_NAME'];
+ $uri = $_SERVER['REQUEST_URI'];
+ $prefix = '';
+
+
+ // Determine prefix
+ $ppos = array(strlen($uri));
+ if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) {
+ $ppos[] = $p;
+ }
+ $prefix = substr($uri, 0, min($ppos));
+
+ // Create absolute URL
+ if($absolute) {
+ $prefix = "http://$server/".trim($prefix, '/');
+ }
+
+ // Put URL together
+ $url = rtrim($prefix, '/').'/'.ltrim($url, '/');
+
+
+ // Return URL
+ return $url;
+ }
+
+ }
+
+?>
diff --git a/core/Logger.inc b/core/Logger.inc
new file mode 100644
index 00000000..8f15caff
--- /dev/null
+++ b/core/Logger.inc
@@ -0,0 +1,143 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Class to log messages to different targets.
+ *
+ * @author coderkun
+ */
+ class Logger
+ {
+ /**
+ * Log mode: Detect automatic
+ *
+ * @var int
+ */
+ const LOGMODE_AUTO = 0;
+ /**
+ * Log mode: Print to screen
+ *
+ * @var int
+ */
+ const LOGMODE_SCREEN = 1;
+ /**
+ * Log mode: Use PHP-logging mechanism
+ *
+ * @var int
+ */
+ const LOGMODE_PHP = 2;
+
+ /**
+ * Do not auto-log to screen
+ *
+ * @var boolean
+ */
+ private $autoLogToScreenDisabled = false;
+
+
+
+
+ /**
+ * Construct a new logger.
+ */
+ public function __construct()
+ {
+ }
+
+
+
+
+ /**
+ * Log a message.
+ *
+ * @param string $message Message to log
+ * @param int $logMode Log mode to use
+ */
+ public function log($message, $logMode=self::LOGMODE_AUTO)
+ {
+ // Choose log mode automatically
+ if($logMode == self::LOGMODE_AUTO) {
+ $logMode = $this->getAutoLogMode();
+ }
+
+ // Print message to screen
+ if($logMode & self::LOGMODE_SCREEN) {
+ $this->logToScreen($message);
+ }
+
+ // Use PHP-logging mechanism
+ if($logMode & self::LOGMODE_PHP) {
+ $this->logToPhp($message);
+ }
+ }
+
+
+ /**
+ * Disable logging to screen when the log mode is automatically
+ * detected.
+ */
+ public function disableAutoLogToScreen()
+ {
+ $this->autoLogToScreenDisabled = true;
+ }
+
+
+
+ /**
+ * Print a message to screen.
+ *
+ * @param string $message Message to print
+ */
+ private function logToScreen($message)
+ {
+ echo "$message \n";
+ }
+
+
+ /**
+ * Log a message by using PHP-logging mechanism.
+ *
+ * @param string $message Message to log
+ */
+ private function logToPhp($message)
+ {
+ error_log($message, 0);
+ }
+
+
+ /**
+ * Detect log mode automatically by distinguishing between
+ * production and test environment.
+ *
+ * @return int Automatically detected log mode
+ */
+ private function getAutoLogMode()
+ {
+ return ($this->isLocalhost() && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP;
+ }
+
+
+ /**
+ * Detect if the server is running as localhost without domain.
+ *
+ * @return boolean Wheather server is running as localhost or not
+ */
+ private function isLocalhost()
+ {
+ return (strpos($_SERVER['HTTP_HOST'], '.') === false);
+ }
+
+ }
+
+?>
diff --git a/core/Model.inc b/core/Model.inc
new file mode 100644
index 00000000..0ede5369
--- /dev/null
+++ b/core/Model.inc
@@ -0,0 +1,141 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Abstract class for implementing a Model.
+ *
+ * @author coderkun
+ */
+ abstract class Model
+ {
+
+
+
+
+ /**
+ * Load the class of a Model.
+ *
+ * @throws \nre\exceptions\ModelNotFoundException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @param string $modelName Name of the Model to load
+ */
+ public static function load($modelName)
+ {
+ // Determine full classname
+ $className = self::getClassName($modelName);
+
+ try {
+ // Load class
+ ClassLoader::load($className);
+
+ // Validate class
+ ClassLoader::check($className, get_class());
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \nre\exceptions\ModelNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \nre\exceptions\ModelNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a Model (Factory Pattern).
+ *
+ * @param string $modelName Name of the Model to instantiate
+ */
+ public static function factory($modelName)
+ {
+ // Determine full classname
+ $className = self::getClassName($modelName);
+
+ // Construct and return Model
+ return new $className();
+ }
+
+
+ /**
+ * Determine the classname for the given Model name.
+ *
+ * @param string $modelName Model name to get classname of
+ * @return string Classname fore the Model name
+ */
+ private static function getClassName($modelName)
+ {
+ $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class()));
+
+
+ return \nre\configs\AppConfig::$app['namespace']."models\\$className";
+ }
+
+
+
+
+ /**
+ * Construct a new Model.
+ *
+ * TODO Catch exception
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ */
+ protected function __construct()
+ {
+ // Load Models
+ $this->loadModels();
+ }
+
+
+
+
+ /**
+ * Load the Models of this Model.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \nre\exceptions\ModelNotValidException
+ * @throws \nre\exceptions\ModelNotFoundException
+ */
+ private function loadModels()
+ {
+ // Determine Models
+ $models = array();
+ if(property_exists($this, 'models')) {
+ $models = $this->models;
+ }
+ if(!is_array($models)) {
+ $models = array($models);
+ }
+
+
+ // Load Models
+ foreach($models as $model)
+ {
+ // Load class
+ Model::load($model);
+
+ // Construct Model
+ $modelName = ucfirst(strtolower($model));
+ $this->$modelName = Model::factory($model);
+ }
+ }
+
+ }
+
+?>
diff --git a/core/Request.inc b/core/Request.inc
new file mode 100644
index 00000000..efd9c834
--- /dev/null
+++ b/core/Request.inc
@@ -0,0 +1,64 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Base class to represent a request.
+ *
+ * @author coderkun
+ */
+ class Request
+ {
+ /**
+ * Passed parameters
+ *
+ * @var array
+ */
+ protected $params = array();
+
+
+
+
+ /**
+ * Get a parameter.
+ *
+ * @param int $index Index of parameter
+ * @param string $defaultIndex Index of default configuration value for this parameter
+ * @return string Value of parameter
+ */
+ public function getParam($index, $defaultIndex=null)
+ {
+ // Return parameter
+ if(count($this->params) > $index) {
+ return $this->params[$index];
+ }
+
+ // Return default value
+ return \nre\core\Config::getDefault($defaultIndex);
+ }
+
+
+ /**
+ * Get all parameters from index on.
+ *
+ * @param int $offset Offset-index
+ * @return array Parameter values
+ */
+ public function getParams($offset=0)
+ {
+ return array_slice($this->params, $offset);
+ }
+
+ }
+
+?>
diff --git a/core/Response.inc b/core/Response.inc
new file mode 100644
index 00000000..e273cba4
--- /dev/null
+++ b/core/Response.inc
@@ -0,0 +1,158 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Base class to represent a response.
+ *
+ * @author coderkun
+ */
+ class Response
+ {
+ /**
+ * Applied parameters
+ *
+ * @var array
+ */
+ protected $params = array();
+ /**
+ * Generated output
+ *
+ * @var string
+ */
+ private $output = '';
+ /**
+ * Abort futher execution
+ *
+ * @var bool
+ */
+ private $exit = false;
+
+
+
+
+ /**
+ * Add a parameter.
+ *
+ * @param mixed $value Value of parameter
+ */
+ public function addParam($value)
+ {
+ $this->params[] = $value;
+ }
+
+
+ /**
+ * Add multiple parameters.
+ *
+ * @param mixed $value1 Value of first parameter
+ * @param mixed … Values of further parameters
+ */
+ public function addParams($value1)
+ {
+ $this->params = array_merge(
+ $this->params,
+ func_get_args()
+ );
+ }
+
+
+ /**
+ * Delete all stored parameters (from offset on).
+ *
+ * @param int $offset Offset-index
+ */
+ public function clearParams($offset=0)
+ {
+ $this->params = array_slice($this->params, 0, $offset);
+ }
+
+
+ /**
+ * Get a parameter.
+ *
+ * @param int $index Index of parameter
+ * @param string $defaultIndex Index of default configuration value for this parameter
+ * @return string Value of parameter
+ */
+ public function getParam($index, $defaultIndex=null)
+ {
+ // Return parameter
+ if(count($this->params) > $index) {
+ return $this->params[$index];
+ }
+
+
+ // Return default value
+ return \nre\core\Config::getDefault($defaultIndex);
+ }
+
+
+ /**
+ * Get all parameters from index on.
+ *
+ * @param int $offset Offset-index
+ * @return array Parameter values
+ */
+ public function getParams($offset=0)
+ {
+ return array_slice($this->params, $offset);
+ }
+
+
+ /**
+ * Set output.
+ *
+ * @param string $output Generated output
+ */
+ public function setOutput($output)
+ {
+ $this->output = $output;
+ }
+
+
+ /**
+ * Get generated output.
+ *
+ * @return string Generated output
+ */
+ public function getOutput()
+ {
+ return $this->output;
+ }
+
+
+ /**
+ * Set exit-state.
+ *
+ * @param bool $exit Abort further execution
+ */
+ public function setExit($exit=true)
+ {
+ $this->exit = $exit;
+ }
+
+
+ /**
+ * Get exit-state.
+ *
+ * @return bool Abort further execution
+ */
+ public function getExit()
+ {
+ return $this->exit;
+ }
+
+ }
+
+?>
diff --git a/core/View.inc b/core/View.inc
new file mode 100644
index 00000000..386d37f8
--- /dev/null
+++ b/core/View.inc
@@ -0,0 +1,124 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * View.
+ *
+ * Class to encapsulate a template file and to provide rendering methods.
+ *
+ * @author coderkun
+ */
+ class View
+ {
+ /**
+ * Template filename
+ *
+ * @var string
+ */
+ protected $templateFilename;
+
+
+
+
+ /**
+ * Load and instantiate the View of an Agent.
+ *
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @param string $layoutName Name of Layout in use
+ * @param string $agentName Name of the Agent
+ * @param string $action Current Action
+ * @param bool $isToplevel Agent is a ToplevelAgent
+ */
+ public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false)
+ {
+ return new View($layoutName, $agentName, $action, $isToplevel);
+ }
+
+
+
+
+ /**
+ * Construct a new View.
+ *
+ * @throws \nre\exceptions\ViewNotFoundException
+ * @param string $layoutName Name of Layout in use
+ * @param string $agentName Name of the Agent
+ * @param string $action Current Action
+ * @param bool $isToplevel Agent is a ToplevelAgent
+ */
+ protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false)
+ {
+ // Create template filename
+ // LayoutName
+ $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS;
+ // AgentName and Action
+ if(strtolower($agentName) != strtolower($layoutName) || !$isToplevel) {
+ $fileName .= strtolower($agentName).DS.strtolower($action);
+ }
+ else {
+ $fileName .= strtolower($layoutName);
+ }
+ // File extension
+ $fileName .= \nre\configs\CoreConfig::getFileExt('views');
+
+
+ // Check template file
+ if(!file_exists($fileName)) {
+ throw new \nre\exceptions\ViewNotFoundException($fileName);
+ }
+
+ // Save filename
+ $this->templateFilename = $fileName;
+ }
+
+
+
+
+ /**
+ * Generate output
+ *
+ * @param array $data Data to have available in the template file
+ */
+ public function render($data)
+ {
+ // Extract data
+ if(!is_null($data)) {
+ extract($data, EXTR_SKIP);
+ }
+
+ // Buffer output
+ ob_start();
+
+ // Include template
+ include($this->templateFilename);
+
+
+ // Return buffered output
+ return ob_get_clean();
+ }
+
+
+ /**
+ * Get template filename.
+ *
+ * @return string Template filename
+ */
+ public function getTemplateFilename()
+ {
+ return $this->templateFilename;
+ }
+
+ }
+
+?>
diff --git a/core/WebUtils.inc b/core/WebUtils.inc
new file mode 100644
index 00000000..ed726576
--- /dev/null
+++ b/core/WebUtils.inc
@@ -0,0 +1,75 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\core;
+
+
+ /**
+ * Class that holds several web-specific methods and properties.
+ *
+ * @author coderkun
+ */
+ class WebUtils
+ {
+ /**
+ * HTTP-statuscode 403: Forbidden
+ *
+ * @var int
+ */
+ const HTTP_FORBIDDEN = 403;
+ /**
+ * HTTP-statuscode 404: Not Found
+ *
+ * @var int
+ */
+ const HTTP_NOT_FOUND = 404;
+ /**
+ * HTTP-statuscode 503: Service Unavailable
+ *
+ * @var int
+ */
+ const HTTP_SERVICE_UNAVAILABLE = 503;
+
+ /**
+ * HTTP-header strings
+ *
+ * @var array
+ */
+ public static $httpStrings = array(
+ 200 => 'OK',
+ 304 => 'Not Modified',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 503 => 'Service Unavailable'
+ );
+
+
+
+
+ /**
+ * Get the HTTP-header of a HTTP-statuscode
+ *
+ * @param int $httpStatusCode HTTP-statuscode
+ * @return string HTTP-header of HTTP-statuscode
+ */
+ public static function getHttpHeader($httpStatusCode)
+ {
+ if(!array_key_exists($httpStatusCode, self::$httpStrings)) {
+ $httpStatusCode = 200;
+ }
+
+
+ return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]);
+ }
+
+ }
+
+?>
diff --git a/db/create.sql b/db/create.sql
new file mode 100644
index 00000000..a1fe59a8
--- /dev/null
+++ b/db/create.sql
@@ -0,0 +1,2320 @@
+-- MySQL dump 10.15 Distrib 10.0.22-MariaDB, for Linux (x86_64)
+--
+-- Host: localhost Database: z
+-- ------------------------------------------------------
+-- Server version 10.0.22-MariaDB-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8mb4 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `achievementconditions`
+--
+
+DROP TABLE IF EXISTS `achievementconditions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievementconditions` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `condition` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `achievementconditions_achievement`
+--
+
+DROP TABLE IF EXISTS `achievementconditions_achievement`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievementconditions_achievement` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `achievement_id` int(11) NOT NULL,
+ `field` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `count` tinyint(1) NOT NULL DEFAULT '0',
+ `value` int(11) NOT NULL,
+ `meta_achievement_id` int(11) DEFAULT NULL,
+ `groupby` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `achievement_id` (`achievement_id`),
+ KEY `meta_achievement_id` (`meta_achievement_id`),
+ CONSTRAINT `achievementconditions_achievement_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `achievementconditions_achievement_ibfk_2` FOREIGN KEY (`achievement_id`) REFERENCES `achievements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `achievementconditions_achievement_ibfk_3` FOREIGN KEY (`meta_achievement_id`) REFERENCES `achievements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `achievementconditions_character`
+--
+
+DROP TABLE IF EXISTS `achievementconditions_character`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievementconditions_character` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `achievement_id` int(11) NOT NULL,
+ `field` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `value` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `achievement_id` (`achievement_id`),
+ CONSTRAINT `achievementconditions_character_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `achievementconditions_character_ibfk_2` FOREIGN KEY (`achievement_id`) REFERENCES `achievements` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `achievementconditions_date`
+--
+
+DROP TABLE IF EXISTS `achievementconditions_date`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievementconditions_date` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `achievement_id` int(11) NOT NULL,
+ `select` varchar(512) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `achievement_id` (`achievement_id`),
+ CONSTRAINT `achievementconditions_date_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `achievementconditions_date_ibfk_2` FOREIGN KEY (`achievement_id`) REFERENCES `achievements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `achievementconditions_quest`
+--
+
+DROP TABLE IF EXISTS `achievementconditions_quest`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievementconditions_quest` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `achievement_id` int(11) NOT NULL,
+ `field` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `count` tinyint(1) NOT NULL DEFAULT '0',
+ `value` int(11) NOT NULL,
+ `quest_id` int(11) DEFAULT NULL,
+ `status` tinyint(3) unsigned DEFAULT NULL,
+ `groupby` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `achievement_id` (`achievement_id`),
+ KEY `quest_id` (`quest_id`),
+ CONSTRAINT `achievementconditions_quest_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `achievementconditions_quest_ibfk_2` FOREIGN KEY (`achievement_id`) REFERENCES `achievements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `achievementconditions_quest_ibfk_3` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `achievements`
+--
+
+DROP TABLE IF EXISTS `achievements`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievements` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `achievementcondition_id` int(11) NOT NULL,
+ `pos` int(10) unsigned NOT NULL DEFAULT '1',
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `description` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `progress` tinyint(1) NOT NULL,
+ `hidden` tinyint(1) NOT NULL DEFAULT '0',
+ `only_once` tinyint(1) NOT NULL DEFAULT '0',
+ `all_conditions` tinyint(1) NOT NULL DEFAULT '1',
+ `deadline` datetime DEFAULT NULL,
+ `unachieved_achievementsmedia_id` int(11) DEFAULT NULL,
+ `achieved_achievementsmedia_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `url` (`seminary_id`,`url`),
+ UNIQUE KEY `pos` (`seminary_id`,`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ KEY `achieved_seminarymedia_id` (`achieved_achievementsmedia_id`),
+ KEY `unachieved_seminarymedia_id` (`unachieved_achievementsmedia_id`),
+ KEY `achievementcondition_id` (`achievementcondition_id`),
+ CONSTRAINT `achievements_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `achievements_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `achievements_ibfk_3` FOREIGN KEY (`unachieved_achievementsmedia_id`) REFERENCES `achievementsmedia` (`seminarymedia_id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `achievements_ibfk_4` FOREIGN KEY (`achieved_achievementsmedia_id`) REFERENCES `achievementsmedia` (`seminarymedia_id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `achievements_ibfk_5` FOREIGN KEY (`achievementcondition_id`) REFERENCES `achievementconditions` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `achievements_characters`
+--
+
+DROP TABLE IF EXISTS `achievements_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievements_characters` (
+ `achievement_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`achievement_id`,`character_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `achievements_characters_ibfk_1` FOREIGN KEY (`achievement_id`) REFERENCES `achievements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `achievements_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `achievementsmedia`
+--
+
+DROP TABLE IF EXISTS `achievementsmedia`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievementsmedia` (
+ `seminarymedia_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ PRIMARY KEY (`seminarymedia_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `achievementsmedia_ibfk_1` FOREIGN KEY (`seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `achievementsmedia_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `avatarpictures`
+--
+
+DROP TABLE IF EXISTS `avatarpictures`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `avatarpictures` (
+ `seminarymedia_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ PRIMARY KEY (`seminarymedia_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `avatarpictures_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `avatarpictures_ibfk_3` FOREIGN KEY (`seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `avatars`
+--
+
+DROP TABLE IF EXISTS `avatars`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `avatars` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `charactertype_id` int(11) NOT NULL,
+ `xplevel_id` int(11) NOT NULL,
+ `avatarpicture_id` int(11) DEFAULT NULL,
+ `small_avatarpicture_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `charactertype_id_2` (`charactertype_id`,`xplevel_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `charactertype_id` (`charactertype_id`),
+ KEY `xplevel_id` (`xplevel_id`),
+ KEY `avatarpicture_id` (`avatarpicture_id`),
+ KEY `small_avatarpicture_id` (`small_avatarpicture_id`),
+ CONSTRAINT `avatars_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `avatars_ibfk_2` FOREIGN KEY (`charactertype_id`) REFERENCES `charactertypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `avatars_ibfk_3` FOREIGN KEY (`xplevel_id`) REFERENCES `xplevels` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `avatars_ibfk_4` FOREIGN KEY (`avatarpicture_id`) REFERENCES `avatarpictures` (`seminarymedia_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `avatars_ibfk_5` FOREIGN KEY (`small_avatarpicture_id`) REFERENCES `avatarpictures` (`seminarymedia_id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `cache_charactergroups`
+--
+
+DROP TABLE IF EXISTS `cache_charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `cache_charactergroups` (
+ `charactergroup_id` int(11) NOT NULL,
+ `xps` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`charactergroup_id`),
+ CONSTRAINT `cache_charactergroups_ibfk_1` FOREIGN KEY (`charactergroup_id`) REFERENCES `charactergroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_cache_charactergroups_insert`
+AFTER INSERT ON cache_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_charactergroup(NEW.charactergroup_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_cache_charactergroups_update`
+AFTER UPDATE ON cache_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_charactergroup(NEW.charactergroup_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `cache_characters`
+--
+
+DROP TABLE IF EXISTS `cache_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `cache_characters` (
+ `character_id` int(11) NOT NULL,
+ `xps` int(10) unsigned NOT NULL,
+ `quest_xps` int(10) unsigned NOT NULL,
+ `xplevel_id` int(11) DEFAULT NULL,
+ `avatar_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`character_id`),
+ KEY `xplevel_id` (`xplevel_id`),
+ KEY `avatar_id` (`avatar_id`),
+ CONSTRAINT `cache_characters_ibfk_1` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `cache_characters_ibfk_2` FOREIGN KEY (`xplevel_id`) REFERENCES `xplevels` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `cache_characters_ibfk_3` FOREIGN KEY (`avatar_id`) REFERENCES `avatars` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `charactergroups`
+--
+
+DROP TABLE IF EXISTS `charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroups` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `charactergroupsgroup_id` int(11) NOT NULL,
+ `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `motto` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `charactergroupsmedia_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `charactergroupsgroup_id_2` (`charactergroupsgroup_id`,`name`),
+ UNIQUE KEY `charactergroupsgroup_id_3` (`charactergroupsgroup_id`,`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `charactergroupsgroup_id` (`charactergroupsgroup_id`),
+ KEY `charactergroupsmedia_id` (`charactergroupsmedia_id`),
+ CONSTRAINT `charactergroups_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `charactergroups_ibfk_3` FOREIGN KEY (`charactergroupsgroup_id`) REFERENCES `charactergroupsgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroups_ibfk_4` FOREIGN KEY (`charactergroupsmedia_id`) REFERENCES `charactergroupsmedia` (`seminarymedia_id`) ON DELETE SET NULL ON UPDATE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Charaktergruppen';
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_charactergroups_insert`
+AFTER INSERT ON charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(NEW.id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `charactergroupsgroups`
+--
+
+DROP TABLE IF EXISTS `charactergroupsgroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsgroups` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `preferred` tinyint(1) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `seminary_id_3` (`seminary_id`,`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ KEY `seminary_id_2` (`seminary_id`),
+ CONSTRAINT `charactergroupsgroups_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `charactergroupsgroups_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `charactergroupsmedia`
+--
+
+DROP TABLE IF EXISTS `charactergroupsmedia`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsmedia` (
+ `seminarymedia_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ PRIMARY KEY (`seminarymedia_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `charactergroupsmedia_ibfk_1` FOREIGN KEY (`seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsmedia_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `charactergroupsquests`
+--
+
+DROP TABLE IF EXISTS `charactergroupsquests`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsquests` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `charactergroupsgroup_id` int(11) NOT NULL,
+ `questgroups_id` int(11) NOT NULL,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `description` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `xps` int(10) unsigned NOT NULL,
+ `rules` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `won_text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `lost_text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `questsmedia_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `charactergroupsgroup_id_2` (`charactergroupsgroup_id`,`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `charactergroupsgroup_id` (`charactergroupsgroup_id`),
+ KEY `questgroups_id` (`questgroups_id`),
+ KEY `questsmedia_id` (`questsmedia_id`),
+ CONSTRAINT `charactergroupsquests_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `charactergroupsquests_ibfk_2` FOREIGN KEY (`charactergroupsgroup_id`) REFERENCES `charactergroupsgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsquests_ibfk_3` FOREIGN KEY (`questgroups_id`) REFERENCES `questgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsquests_ibfk_4` FOREIGN KEY (`questsmedia_id`) REFERENCES `questsmedia` (`media_id`) ON DELETE SET NULL ON UPDATE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_charactergroupsquests_update`
+AFTER UPDATE ON charactergroupsquests
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups_charactergroupsquest(NEW.id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `charactergroupsquests_groups`
+--
+
+DROP TABLE IF EXISTS `charactergroupsquests_groups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsquests_groups` (
+ `charactergroupsquest_id` int(11) NOT NULL,
+ `charactergroup_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `xps_factor` float NOT NULL,
+ PRIMARY KEY (`charactergroupsquest_id`,`charactergroup_id`),
+ KEY `charactergroup_id` (`charactergroup_id`),
+ CONSTRAINT `charactergroupsquests_groups_ibfk_1` FOREIGN KEY (`charactergroupsquest_id`) REFERENCES `charactergroupsquests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsquests_groups_ibfk_2` FOREIGN KEY (`charactergroup_id`) REFERENCES `charactergroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_charactergroupsquests_groups_insert`
+AFTER INSERT ON charactergroupsquests_groups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(NEW.charactergroup_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_charactergroupsquests_groups_update`
+AFTER UPDATE ON charactergroupsquests_groups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(OLD.charactergroup_id);
+ CALL update_cache_charactergroups(NEW.charactergroup_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_charactergroupsquests_groups_delete`
+AFTER DELETE ON charactergroupsquests_groups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(OLD.charactergroup_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `charactergroupsquests_seminaryuploads`
+--
+
+DROP TABLE IF EXISTS `charactergroupsquests_seminaryuploads`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsquests_seminaryuploads` (
+ `seminaryupload_id` int(11) NOT NULL,
+ `charactergroupsquest_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ PRIMARY KEY (`seminaryupload_id`),
+ KEY `charactergroupsquest_id` (`charactergroupsquest_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `charactergroupsquests_seminaryuploads_ibfk_1` FOREIGN KEY (`seminaryupload_id`) REFERENCES `seminaryuploads` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsquests_seminaryuploads_ibfk_2` FOREIGN KEY (`charactergroupsquest_id`) REFERENCES `charactergroupsquests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsquests_seminaryuploads_ibfk_3` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `characterroles`
+--
+
+DROP TABLE IF EXISTS `characterroles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `characterroles` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `characters`
+--
+
+DROP TABLE IF EXISTS `characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `characters` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `user_id` int(11) NOT NULL,
+ `charactertype_id` int(11) NOT NULL,
+ `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`),
+ KEY `charactertype_id` (`charactertype_id`),
+ KEY `user_id` (`user_id`),
+ CONSTRAINT `characters_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `characters_ibfk_2` FOREIGN KEY (`charactertype_id`) REFERENCES `charactertypes` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Charaktere';
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_characters_insert`
+AFTER INSERT ON characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(NEW.id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `characters_charactergroups`
+--
+
+DROP TABLE IF EXISTS `characters_charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `characters_charactergroups` (
+ `character_id` int(11) NOT NULL,
+ `charactergroup_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`character_id`,`charactergroup_id`),
+ KEY `charactergroup_id` (`charactergroup_id`),
+ CONSTRAINT `characters_charactergroups_ibfk_1` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `characters_charactergroups_ibfk_2` FOREIGN KEY (`charactergroup_id`) REFERENCES `charactergroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_characters_charactergroups_insert`
+AFTER INSERT ON characters_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(NEW.character_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_characters_charactergroups_update`
+AFTER UPDATE ON characters_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+ CALL update_cache_characters(NEW.character_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_characters_charactergroups_delete`
+AFTER DELETE ON characters_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `characters_characterroles`
+--
+
+DROP TABLE IF EXISTS `characters_characterroles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `characters_characterroles` (
+ `character_id` int(11) NOT NULL,
+ `characterrole_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`character_id`,`characterrole_id`),
+ KEY `userseminaryrole_id` (`characterrole_id`),
+ CONSTRAINT `characters_characterroles_ibfk_1` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `characters_characterroles_ibfk_2` FOREIGN KEY (`characterrole_id`) REFERENCES `characterroles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `characters_seminarycharacterfields`
+--
+
+DROP TABLE IF EXISTS `characters_seminarycharacterfields`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `characters_seminarycharacterfields` (
+ `character_id` int(11) NOT NULL,
+ `seminarycharacterfield_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `value` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`character_id`,`seminarycharacterfield_id`),
+ KEY `seminarycharacterfield_id` (`seminarycharacterfield_id`),
+ CONSTRAINT `characters_seminarycharacterfields_ibfk_1` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `characters_seminarycharacterfields_ibfk_2` FOREIGN KEY (`seminarycharacterfield_id`) REFERENCES `seminarycharacterfields` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `charactertypes`
+--
+
+DROP TABLE IF EXISTS `charactertypes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactertypes` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`,`seminary_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `charactertypes_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `charactertypes_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Charaktertypen';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `maps`
+--
+
+DROP TABLE IF EXISTS `maps`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `maps` (
+ `seminary_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `seminarymedia_id` int(11) NOT NULL,
+ `width` int(10) unsigned NOT NULL,
+ `height` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`seminary_id`),
+ KEY `seminarymedia_id` (`seminarymedia_id`),
+ CONSTRAINT `maps_ibfk_1` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`),
+ CONSTRAINT `maps_ibfk_2` FOREIGN KEY (`seminarymedia_id`) REFERENCES `seminarymedia` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `media`
+--
+
+DROP TABLE IF EXISTS `media`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `media` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `description` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `mimetype` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `seminary_id_2` (`name`),
+ UNIQUE KEY `seminary_id_3` (`url`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `media_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `pages`
+--
+
+DROP TABLE IF EXISTS `pages`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `pages` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `pos` int(11) NOT NULL,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `url` (`url`),
+ UNIQUE KEY `pos` (`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `pages_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questgroups`
+--
+
+DROP TABLE IF EXISTS `questgroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questgroups` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `title` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `questgroupspicture_id` int(11) DEFAULT NULL,
+ `achievable_xps` int(10) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `title` (`seminary_id`,`title`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questgroupspicture_id` (`questgroupspicture_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `questgroups_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questgroups_ibfk_4` FOREIGN KEY (`questgroupspicture_id`) REFERENCES `questgroupspictures` (`media_id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `questgroups_ibfk_5` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Aufgabengruppen';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questgroups_characters`
+--
+
+DROP TABLE IF EXISTS `questgroups_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questgroups_characters` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `questgroup_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `status` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `questgroup_id` (`questgroup_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `questgroups_characters_ibfk_1` FOREIGN KEY (`questgroup_id`) REFERENCES `questgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questgroups_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questgroups_questgroupshierarchy`
+--
+
+DROP TABLE IF EXISTS `questgroups_questgroupshierarchy`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questgroups_questgroupshierarchy` (
+ `questgroup_id` int(11) NOT NULL,
+ `questgroupshierarchy_id` int(11) NOT NULL,
+ `parent_questgroup_id` int(11) DEFAULT NULL,
+ `pos` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`questgroup_id`),
+ UNIQUE KEY `pos` (`parent_questgroup_id`,`pos`),
+ KEY `parent_questgoup_id` (`parent_questgroup_id`),
+ KEY `questgroupshierarchy_id` (`questgroupshierarchy_id`),
+ CONSTRAINT `questgroups_questgroupshierarchy_ibfk_1` FOREIGN KEY (`questgroup_id`) REFERENCES `questgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questgroups_questgroupshierarchy_ibfk_2` FOREIGN KEY (`questgroupshierarchy_id`) REFERENCES `questgroupshierarchy` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questgroups_questgroupshierarchy_ibfk_3` FOREIGN KEY (`parent_questgroup_id`) REFERENCES `questgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questgroups_questtexts`
+--
+
+DROP TABLE IF EXISTS `questgroups_questtexts`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questgroups_questtexts` (
+ `questgroup_id` int(11) NOT NULL,
+ `questtext_id` int(11) NOT NULL,
+ `entry_text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`questgroup_id`,`questtext_id`),
+ KEY `questtext_id` (`questtext_id`),
+ CONSTRAINT `questgroups_questtexts_ibfk_1` FOREIGN KEY (`questgroup_id`) REFERENCES `questgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questgroups_questtexts_ibfk_2` FOREIGN KEY (`questtext_id`) REFERENCES `questtexts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questgroupshierarchy`
+--
+
+DROP TABLE IF EXISTS `questgroupshierarchy`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questgroupshierarchy` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `parent_questgroupshierarchy_id` int(11) DEFAULT NULL,
+ `pos` int(10) unsigned NOT NULL,
+ `title_singular` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `title_plural` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `pos` (`parent_questgroupshierarchy_id`,`pos`),
+ UNIQUE KEY `url` (`seminary_id`,`parent_questgroupshierarchy_id`,`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ KEY `parent_questhierarchy_id` (`parent_questgroupshierarchy_id`),
+ CONSTRAINT `questgroupshierarchy_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questgroupshierarchy_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questgroupshierarchy_ibfk_3` FOREIGN KEY (`parent_questgroupshierarchy_id`) REFERENCES `questgroupshierarchy` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Aufgabengruppenhierarchie';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questgroupspictures`
+--
+
+DROP TABLE IF EXISTS `questgroupspictures`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questgroupspictures` (
+ `media_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ PRIMARY KEY (`media_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `questgroupspictures_ibfk_1` FOREIGN KEY (`media_id`) REFERENCES `seminarymedia` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questgroupspictures_ibfk_3` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questgrouptexts`
+--
+
+DROP TABLE IF EXISTS `questgrouptexts`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questgrouptexts` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `questgroup_id` int(11) NOT NULL,
+ `pos` int(11) NOT NULL DEFAULT '1',
+ `text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `questgroup_id_2` (`questgroup_id`,`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questgroup_id` (`questgroup_id`),
+ CONSTRAINT `questgrouptexts_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questgrouptexts_ibfk_2` FOREIGN KEY (`questgroup_id`) REFERENCES `questgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `quests`
+--
+
+DROP TABLE IF EXISTS `quests`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `quests` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `questgroup_id` int(11) NOT NULL,
+ `questtype_id` int(11) NOT NULL,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `xps` int(11) unsigned NOT NULL,
+ `entry_text` text COLLATE utf8mb4_unicode_ci,
+ `wrong_text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `task` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `questsmedia_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `questgroup_id_url` (`questgroup_id`,`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questtype_id` (`questtype_id`),
+ KEY `questmedia_id` (`questsmedia_id`),
+ KEY `questgroup_id` (`questgroup_id`),
+ CONSTRAINT `quests_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `quests_ibfk_2` FOREIGN KEY (`questtype_id`) REFERENCES `questtypes` (`id`),
+ CONSTRAINT `quests_ibfk_4` FOREIGN KEY (`questgroup_id`) REFERENCES `questgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `quests_ibfk_5` FOREIGN KEY (`questsmedia_id`) REFERENCES `questsmedia` (`media_id`) ON DELETE SET NULL ON UPDATE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_quests_update`
+AFTER UPDATE ON quests
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_quest(NEW.id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `quests_characters`
+--
+
+DROP TABLE IF EXISTS `quests_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `quests_characters` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `quest_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `status` tinyint(4) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quest_id` (`quest_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `quests_characters_ibfk_1` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `quests_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_quests_characters_insert`
+AFTER INSERT ON quests_characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(NEW.character_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_quests_characters_update`
+AFTER UPDATE ON quests_characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+ CALL update_cache_characters(NEW.character_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_quests_characters_delete`
+AFTER DELETE ON quests_characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Table structure for table `quests_previousquests`
+--
+
+DROP TABLE IF EXISTS `quests_previousquests`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `quests_previousquests` (
+ `quest_id` int(11) NOT NULL,
+ `previous_quest_id` int(11) NOT NULL,
+ PRIMARY KEY (`quest_id`,`previous_quest_id`),
+ KEY `previous_quest_id` (`previous_quest_id`),
+ CONSTRAINT `quests_previousquests_ibfk_1` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `quests_previousquests_ibfk_2` FOREIGN KEY (`previous_quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Vorherige Aufgaben';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `quests_questsubtopics`
+--
+
+DROP TABLE IF EXISTS `quests_questsubtopics`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `quests_questsubtopics` (
+ `quest_id` int(11) NOT NULL,
+ `questsubtopic_id` int(11) NOT NULL,
+ PRIMARY KEY (`quest_id`,`questsubtopic_id`),
+ KEY `questsubtopic_id` (`questsubtopic_id`),
+ CONSTRAINT `quests_questsubtopics_ibfk_3` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `quests_questsubtopics_ibfk_4` FOREIGN KEY (`questsubtopic_id`) REFERENCES `questsubtopics` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questsmedia`
+--
+
+DROP TABLE IF EXISTS `questsmedia`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questsmedia` (
+ `media_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ PRIMARY KEY (`media_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `questsmedia_ibfk_1` FOREIGN KEY (`media_id`) REFERENCES `seminarymedia` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questsmedia_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questsubtopics`
+--
+
+DROP TABLE IF EXISTS `questsubtopics`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questsubtopics` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `questtopic_id` int(11) NOT NULL,
+ `title` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `title` (`title`,`questtopic_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questtopic_id` (`questtopic_id`),
+ CONSTRAINT `questsubtopics_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questsubtopics_ibfk_2` FOREIGN KEY (`questtopic_id`) REFERENCES `questtopics` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Aufgabenbereiche';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtexts`
+--
+
+DROP TABLE IF EXISTS `questtexts`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtexts` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `quest_id` int(11) NOT NULL,
+ `questtexttype_id` int(11) NOT NULL,
+ `pos` int(11) NOT NULL DEFAULT '1',
+ `text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `out_text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `abort_text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `questsmedia_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `quest_id_2` (`quest_id`,`questtexttype_id`,`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `quest_id` (`quest_id`),
+ KEY `questtexttype_id` (`questtexttype_id`),
+ KEY `questsmedia_id` (`questsmedia_id`),
+ CONSTRAINT `questtexts_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtexts_ibfk_3` FOREIGN KEY (`questtexttype_id`) REFERENCES `questtexttypes` (`id`),
+ CONSTRAINT `questtexts_ibfk_4` FOREIGN KEY (`questsmedia_id`) REFERENCES `questsmedia` (`media_id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `questtexts_ibfk_5` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtexttypes`
+--
+
+DROP TABLE IF EXISTS `questtexttypes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtexttypes` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `type` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `type` (`type`),
+ UNIQUE KEY `url` (`url`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtopics`
+--
+
+DROP TABLE IF EXISTS `questtopics`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtopics` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `url` (`seminary_id`,`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `questtopics_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtopics_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Aufgabenbereiche';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes`
+--
+
+DROP TABLE IF EXISTS `questtypes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `classname` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `title` (`title`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_bossfight`
+--
+
+DROP TABLE IF EXISTS `questtypes_bossfight`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_bossfight` (
+ `quest_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `bossname` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `boss_seminarymedia_id` int(11) NOT NULL,
+ `lives_character` smallint(5) unsigned NOT NULL,
+ `lives_boss` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`quest_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `boss_seminarymedia_id` (`boss_seminarymedia_id`),
+ CONSTRAINT `questtypes_bossfight_ibfk_1` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_bossfight_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtypes_bossfight_ibfk_3` FOREIGN KEY (`boss_seminarymedia_id`) REFERENCES `seminarymedia` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_bossfight_stages`
+--
+
+DROP TABLE IF EXISTS `questtypes_bossfight_stages`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_bossfight_stages` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `questtypes_bossfight_quest_id` int(11) NOT NULL,
+ `parent_stage_id` int(11) DEFAULT NULL,
+ `text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `question` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `livedrain_character` smallint(6) NOT NULL,
+ `livedrain_boss` smallint(6) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `parent_stage_id` (`parent_stage_id`),
+ KEY `questtypes_bossfight_quest_id` (`questtypes_bossfight_quest_id`),
+ CONSTRAINT `questtypes_bossfight_stages_ibfk_1` FOREIGN KEY (`questtypes_bossfight_quest_id`) REFERENCES `questtypes_bossfight` (`quest_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_bossfight_stages_ibfk_2` FOREIGN KEY (`parent_stage_id`) REFERENCES `questtypes_bossfight_stages` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_bossfight_stages_characters`
+--
+
+DROP TABLE IF EXISTS `questtypes_bossfight_stages_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_bossfight_stages_characters` (
+ `questtypes_bossfight_stage_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`questtypes_bossfight_stage_id`,`character_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `questtypes_bossfight_stages_characters_ibfk_1` FOREIGN KEY (`questtypes_bossfight_stage_id`) REFERENCES `questtypes_bossfight_stages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_bossfight_stages_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_choiceinput`
+--
+
+DROP TABLE IF EXISTS `questtypes_choiceinput`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_choiceinput` (
+ `quest_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`quest_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `questtypes_choiceinput_ibfk_1` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_choiceinput_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_choiceinput_choices`
+--
+
+DROP TABLE IF EXISTS `questtypes_choiceinput_choices`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_choiceinput_choices` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `questtypes_choiceinput_list_id` int(11) NOT NULL,
+ `pos` int(10) unsigned NOT NULL DEFAULT '1',
+ `text` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `questtypes_choiceinput_list_id` (`questtypes_choiceinput_list_id`,`pos`),
+ CONSTRAINT `questtypes_choiceinput_choices_ibfk_1` FOREIGN KEY (`questtypes_choiceinput_list_id`) REFERENCES `questtypes_choiceinput_lists` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_choiceinput_lists`
+--
+
+DROP TABLE IF EXISTS `questtypes_choiceinput_lists`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_choiceinput_lists` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `questtypes_choiceinput_quest_id` int(11) NOT NULL,
+ `number` int(10) unsigned NOT NULL,
+ `questtypes_choiceinput_choice_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `questtypes_textinput_quest_id` (`questtypes_choiceinput_quest_id`,`number`),
+ KEY `questtypes_choiceinput_choice_id` (`questtypes_choiceinput_choice_id`),
+ CONSTRAINT `questtypes_choiceinput_lists_ibfk_1` FOREIGN KEY (`questtypes_choiceinput_quest_id`) REFERENCES `questtypes_choiceinput` (`quest_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_choiceinput_lists_ibfk_2` FOREIGN KEY (`questtypes_choiceinput_choice_id`) REFERENCES `questtypes_choiceinput_choices` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_choiceinput_lists_characters`
+--
+
+DROP TABLE IF EXISTS `questtypes_choiceinput_lists_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_choiceinput_lists_characters` (
+ `character_id` int(11) NOT NULL,
+ `questtypes_choiceinput_list_id` int(11) NOT NULL,
+ `questtypes_choiceinput_choice_id` int(11) DEFAULT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`character_id`,`questtypes_choiceinput_list_id`),
+ KEY `questtypes_choiceinput_choice_id` (`questtypes_choiceinput_choice_id`),
+ KEY `questtypes_choiceinput_list_id` (`questtypes_choiceinput_list_id`),
+ CONSTRAINT `questtypes_choiceinput_lists_characters_ibfk_1` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_choiceinput_lists_characters_ibfk_2` FOREIGN KEY (`questtypes_choiceinput_list_id`) REFERENCES `questtypes_choiceinput_lists` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_choiceinput_lists_characters_ibfk_3` FOREIGN KEY (`questtypes_choiceinput_choice_id`) REFERENCES `questtypes_choiceinput_choices` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_crossword_words`
+--
+
+DROP TABLE IF EXISTS `questtypes_crossword_words`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_crossword_words` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `quest_id` int(11) NOT NULL,
+ `question` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `word` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `vertical` tinyint(1) NOT NULL,
+ `pos_x` int(10) unsigned NOT NULL,
+ `pos_y` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `quest_id_2` (`quest_id`,`vertical`,`pos_x`,`pos_y`),
+ KEY `quest_id` (`quest_id`),
+ CONSTRAINT `questtypes_crossword_words_ibfk_1` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_crossword_words_characters`
+--
+
+DROP TABLE IF EXISTS `questtypes_crossword_words_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_crossword_words_characters` (
+ `questtypes_crossword_word_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `answer` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`questtypes_crossword_word_id`,`character_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `questtypes_crossword_words_characters_ibfk_1` FOREIGN KEY (`questtypes_crossword_word_id`) REFERENCES `questtypes_crossword_words` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_crossword_words_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_dragndrop`
+--
+
+DROP TABLE IF EXISTS `questtypes_dragndrop`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_dragndrop` (
+ `quest_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `questmedia_id` int(11) NOT NULL,
+ `width` int(10) unsigned NOT NULL,
+ `height` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`quest_id`),
+ KEY `questmedia_id` (`questmedia_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `questtypes_dragndrop_ibfk_1` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_dragndrop_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtypes_dragndrop_ibfk_3` FOREIGN KEY (`questmedia_id`) REFERENCES `questsmedia` (`media_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_dragndrop_drags`
+--
+
+DROP TABLE IF EXISTS `questtypes_dragndrop_drags`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_dragndrop_drags` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `questtypes_dragndrop_id` int(11) NOT NULL,
+ `questmedia_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `questtypes_dragndrop_id` (`questtypes_dragndrop_id`),
+ KEY `questmedia_id` (`questmedia_id`),
+ CONSTRAINT `questtypes_dragndrop_drags_ibfk_1` FOREIGN KEY (`questtypes_dragndrop_id`) REFERENCES `questtypes_dragndrop` (`quest_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_dragndrop_drags_ibfk_2` FOREIGN KEY (`questmedia_id`) REFERENCES `questsmedia` (`media_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_dragndrop_drops`
+--
+
+DROP TABLE IF EXISTS `questtypes_dragndrop_drops`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_dragndrop_drops` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `questtypes_dragndrop_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `top` int(11) NOT NULL,
+ `left` int(11) NOT NULL,
+ `width` int(10) unsigned NOT NULL,
+ `height` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `questtypes_dragndrop_id` (`questtypes_dragndrop_id`),
+ CONSTRAINT `questtypes_dragndrop_drops_ibfk_4` FOREIGN KEY (`questtypes_dragndrop_id`) REFERENCES `questtypes_dragndrop` (`quest_id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_dragndrop_drops_characters`
+--
+
+DROP TABLE IF EXISTS `questtypes_dragndrop_drops_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_dragndrop_drops_characters` (
+ `questtypes_dragndrop_drop_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `questtypes_dragndrop_drag_id` int(11) NOT NULL,
+ PRIMARY KEY (`questtypes_dragndrop_drop_id`,`character_id`),
+ KEY `questtypes_dragndrop_drag_id` (`questtypes_dragndrop_drag_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `questtypes_dragndrop_drops_characters_ibfk_1` FOREIGN KEY (`questtypes_dragndrop_drop_id`) REFERENCES `questtypes_dragndrop_drops` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_dragndrop_drops_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_dragndrop_drops_characters_ibfk_3` FOREIGN KEY (`questtypes_dragndrop_drag_id`) REFERENCES `questtypes_dragndrop_drags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_dragndrop_drops_drags`
+--
+
+DROP TABLE IF EXISTS `questtypes_dragndrop_drops_drags`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_dragndrop_drops_drags` (
+ `questtypes_dragndrop_drop_id` int(11) NOT NULL,
+ `questtypes_dragndrop_drag_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ PRIMARY KEY (`questtypes_dragndrop_drop_id`,`questtypes_dragndrop_drag_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questtypes_dragndrop_drag_id` (`questtypes_dragndrop_drag_id`),
+ CONSTRAINT `questtypes_dragndrop_drops_drags_ibfk_1` FOREIGN KEY (`questtypes_dragndrop_drop_id`) REFERENCES `questtypes_dragndrop_drops` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_dragndrop_drops_drags_ibfk_3` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtypes_dragndrop_drops_drags_ibfk_4` FOREIGN KEY (`questtypes_dragndrop_drag_id`) REFERENCES `questtypes_dragndrop_drags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_multiplechoice`
+--
+
+DROP TABLE IF EXISTS `questtypes_multiplechoice`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_multiplechoice` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `quest_id` int(11) NOT NULL,
+ `pos` int(10) unsigned NOT NULL DEFAULT '1',
+ `question` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `quest_id_2` (`quest_id`,`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `quest_id` (`quest_id`),
+ CONSTRAINT `questtypes_multiplechoice_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtypes_multiplechoice_ibfk_2` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_multiplechoice_answers`
+--
+
+DROP TABLE IF EXISTS `questtypes_multiplechoice_answers`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_multiplechoice_answers` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `questtypes_multiplechoice_id` int(11) NOT NULL,
+ `pos` int(11) NOT NULL,
+ `answer` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `tick` tinyint(1) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `questtypes_multiplechoice_id_2` (`questtypes_multiplechoice_id`,`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questtypes_multiplechoice_id` (`questtypes_multiplechoice_id`),
+ CONSTRAINT `questtypes_multiplechoice_answers_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtypes_multiplechoice_answers_ibfk_2` FOREIGN KEY (`questtypes_multiplechoice_id`) REFERENCES `questtypes_multiplechoice` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_multiplechoice_characters`
+--
+
+DROP TABLE IF EXISTS `questtypes_multiplechoice_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_multiplechoice_characters` (
+ `questtypes_multiplechoice_answer_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `ticked` tinyint(1) NOT NULL,
+ PRIMARY KEY (`questtypes_multiplechoice_answer_id`,`character_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `questtypes_multiplechoice_characters_ibfk_1` FOREIGN KEY (`questtypes_multiplechoice_answer_id`) REFERENCES `questtypes_multiplechoice_answers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_multiplechoice_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_submit_characters`
+--
+
+DROP TABLE IF EXISTS `questtypes_submit_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_submit_characters` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `quest_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `upload_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `character_id` (`character_id`),
+ KEY `upload_id` (`upload_id`),
+ KEY `quest_id` (`quest_id`),
+ CONSTRAINT `questtypes_submit_characters_ibfk_3` FOREIGN KEY (`upload_id`) REFERENCES `seminaryuploads` (`id`),
+ CONSTRAINT `questtypes_submit_characters_ibfk_4` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_submit_characters_ibfk_5` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_submit_characters_comments`
+--
+
+DROP TABLE IF EXISTS `questtypes_submit_characters_comments`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_submit_characters_comments` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `questtypes_submit_character_id` int(11) NOT NULL,
+ `comment` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questtypes_submit_character_id` (`questtypes_submit_character_id`),
+ CONSTRAINT `questtypes_submit_characters_comments_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtypes_submit_characters_comments_ibfk_2` FOREIGN KEY (`questtypes_submit_character_id`) REFERENCES `questtypes_submit_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_submit_mimetypes`
+--
+
+DROP TABLE IF EXISTS `questtypes_submit_mimetypes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_submit_mimetypes` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `mimetype` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `size` int(10) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `mimetype` (`mimetype`,`seminary_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `questtypes_submit_mimetypes_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `questtypes_submit_mimetypes_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_textinput`
+--
+
+DROP TABLE IF EXISTS `questtypes_textinput`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_textinput` (
+ `quest_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `text` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`quest_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `questtypes_textinput_ibfk_1` FOREIGN KEY (`quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_textinput_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_textinput_fields`
+--
+
+DROP TABLE IF EXISTS `questtypes_textinput_fields`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_textinput_fields` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `questtypes_textinput_quest_id` int(11) NOT NULL,
+ `number` int(10) unsigned NOT NULL,
+ `questtypes_textinput_fieldsize_id` int(11) NOT NULL DEFAULT '1',
+ `regex` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `questtypes_textinput_quest_id` (`questtypes_textinput_quest_id`,`number`),
+ KEY `questtypes_textinput_fieldsize_id` (`questtypes_textinput_fieldsize_id`),
+ CONSTRAINT `questtypes_textinput_fields_ibfk_1` FOREIGN KEY (`questtypes_textinput_quest_id`) REFERENCES `quests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_textinput_fields_ibfk_2` FOREIGN KEY (`questtypes_textinput_fieldsize_id`) REFERENCES `questtypes_textinput_fieldsizes` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_textinput_fields_characters`
+--
+
+DROP TABLE IF EXISTS `questtypes_textinput_fields_characters`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_textinput_fields_characters` (
+ `questtypes_textinput_field_id` int(11) NOT NULL,
+ `character_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `value` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`questtypes_textinput_field_id`,`character_id`),
+ KEY `character_id` (`character_id`),
+ CONSTRAINT `questtypes_textinput_fields_characters_ibfk_2` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `questtypes_textinput_fields_characters_ibfk_3` FOREIGN KEY (`questtypes_textinput_field_id`) REFERENCES `questtypes_textinput_fields` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `questtypes_textinput_fieldsizes`
+--
+
+DROP TABLE IF EXISTS `questtypes_textinput_fieldsizes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `questtypes_textinput_fieldsizes` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `size` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `size` (`size`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `seminaries`
+--
+
+DROP TABLE IF EXISTS `seminaries`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `seminaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `title` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `course` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `description` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `seminarymedia_id` int(11) DEFAULT NULL,
+ `charactergroups_seminarymedia_id` int(11) DEFAULT NULL,
+ `achievements_seminarymedia_id` int(11) DEFAULT NULL,
+ `library_seminarymedia_id` int(11) DEFAULT NULL,
+ `map_seminarymedia_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `title` (`title`),
+ UNIQUE KEY `url` (`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `media_id` (`seminarymedia_id`),
+ KEY `charactergroups_seminarymedia_id` (`charactergroups_seminarymedia_id`),
+ KEY `achievements_seminarymedia_id` (`achievements_seminarymedia_id`),
+ KEY `library_seminarymedia_id` (`library_seminarymedia_id`),
+ KEY `map_seminarymedia_id` (`map_seminarymedia_id`),
+ CONSTRAINT `seminaries_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `seminaries_ibfk_2` FOREIGN KEY (`seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `seminaries_ibfk_3` FOREIGN KEY (`charactergroups_seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `seminaries_ibfk_4` FOREIGN KEY (`achievements_seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `seminaries_ibfk_5` FOREIGN KEY (`library_seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `seminaries_ibfk_6` FOREIGN KEY (`map_seminarymedia_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lehrveranstaltungen';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `seminarycharacterfields`
+--
+
+DROP TABLE IF EXISTS `seminarycharacterfields`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `seminarycharacterfields` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `pos` int(10) unsigned NOT NULL,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `seminarycharacterfieldtype_id` int(11) NOT NULL,
+ `regex` varchar(512) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `required` tinyint(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `url` (`seminary_id`,`url`),
+ UNIQUE KEY `seminary_id_2` (`seminary_id`,`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ KEY `seminarycharacterfieldtype_id` (`seminarycharacterfieldtype_id`),
+ CONSTRAINT `seminarycharacterfields_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `seminarycharacterfields_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `seminarycharacterfields_ibfk_3` FOREIGN KEY (`seminarycharacterfieldtype_id`) REFERENCES `seminarycharacterfieldtypes` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `seminarycharacterfieldtypes`
+--
+
+DROP TABLE IF EXISTS `seminarycharacterfieldtypes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `seminarycharacterfieldtypes` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `seminarymedia`
+--
+
+DROP TABLE IF EXISTS `seminarymedia`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `seminarymedia` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `description` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `sourceurl` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `mimetype` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `seminary_id_2` (`seminary_id`,`name`),
+ UNIQUE KEY `seminary_id_3` (`seminary_id`,`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `seminarymedia_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `seminarymedia_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `seminaryuploads`
+--
+
+DROP TABLE IF EXISTS `seminaryuploads`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `seminaryuploads` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) DEFAULT NULL,
+ `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `mimetype` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `public` tinyint(1) NOT NULL DEFAULT '1',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `url` (`url`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `seminaryuploads_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `seminaryuploads_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `userroles`
+--
+
+DROP TABLE IF EXISTS `userroles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `userroles` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `users` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `username` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `surname` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `prename` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `email` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `mailing` tinyint(1) NOT NULL DEFAULT '1',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `username` (`username`),
+ UNIQUE KEY `email` (`email`),
+ UNIQUE KEY `url` (`url`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Benutzer';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `users_userroles`
+--
+
+DROP TABLE IF EXISTS `users_userroles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `users_userroles` (
+ `user_id` int(11) NOT NULL,
+ `userrole_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`user_id`,`userrole_id`),
+ KEY `userrole_id` (`userrole_id`),
+ CONSTRAINT `users_userroles_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `users_userroles_ibfk_3` FOREIGN KEY (`userrole_id`) REFERENCES `userroles` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `xplevels`
+--
+
+DROP TABLE IF EXISTS `xplevels`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `xplevels` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `seminary_id` int(11) NOT NULL,
+ `xps` int(10) unsigned NOT NULL,
+ `level` int(11) NOT NULL,
+ `name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `xps` (`seminary_id`,`xps`),
+ UNIQUE KEY `level` (`level`,`seminary_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `xplevels_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `xplevels_ibfk_2` FOREIGN KEY (`seminary_id`) REFERENCES `seminaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_characters_xplevels_insert`
+AFTER INSERT ON xplevels
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_xplevels(NEW.seminary_id, NEW.xps, NULL);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_characters_xplevels_update`
+AFTER UPDATE ON xplevels
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_xplevels(NEW.seminary_id, OLD.xps, NEW.xps);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`z`@`%`*/ /*!50003 TRIGGER `update_cache_characters_xplevels_delete`
+AFTER DELETE ON xplevels
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_xplevels(OLD.seminary_id, OLD.xps, NULL);
+END */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+--
+-- Dumping routines for database 'z'
+--
+/*!50003 DROP PROCEDURE IF EXISTS `update_cache_charactergroups` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`z`@`%` PROCEDURE `update_cache_charactergroups`(
+ IN CHARACTERGROUPID INT(11)
+)
+BEGIN
+
+ REPLACE INTO cache_charactergroups
+ (charactergroup_id, xps)
+ SELECT charactergroups.id, IFNULL(SUM(ROUND(charactergroupsquests.xps * charactergroupsquests_groups.xps_factor)),0) AS xps
+ FROM charactergroups
+ LEFT JOIN charactergroupsquests_groups ON charactergroupsquests_groups.charactergroup_id = charactergroups.id
+ LEFT JOIN charactergroupsquests ON charactergroupsquests.id = charactergroupsquests_groups.charactergroupsquest_id
+ WHERE charactergroups.id = CHARACTERGROUPID;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `update_cache_charactergroups_charactergroupsquest` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`z`@`%` PROCEDURE `update_cache_charactergroups_charactergroupsquest`(
+ IN CHARACTERGROUPSQUESTID INT(11)
+)
+BEGIN
+ DECLARE CHARACTERGROUPID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE charactergroups_cursor CURSOR FOR
+ SELECT charactergroup_id
+ FROM charactergroupsquests_groups
+ WHERE charactergroupsquest_id = CHARACTERGROUPSQUESTID;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ SET done = 0;
+ OPEN charactergroups_cursor;
+ get_charactergroups: LOOP
+ FETCH charactergroups_cursor INTO CHARACTERGROUPID;
+ IF done = TRUE THEN
+ LEAVE get_charactergroups;
+ END IF;
+ CALL update_cache_charactergroups(CHARACTERGROUPID);
+ END LOOP get_charactergroups;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `update_cache_characters` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`z`@`%` PROCEDURE `update_cache_characters`(
+ IN CHARACTERID INT(11)
+)
+BEGIN
+ DECLARE QUESTXPS INT UNSIGNED;
+ DECLARE GROUPSXPS INT UNSIGNED;
+ DECLARE TOTALXPS INT UNSIGNED;
+ DECLARE XPLEVELID INT;
+ DECLARE AVATARID INT;
+
+
+ SELECT IFNULL(SUM(quests.xps),0) INTO QUESTXPS
+ FROM quests_characters
+ LEFT JOIN quests ON quests.id = quests_characters.quest_id
+ WHERE quests_characters.character_id = CHARACTERID AND quests_characters.status = 3;
+ SELECT IFNULL(SUM(cache_charactergroups.xps),0) INTO GROUPSXPS
+ FROM characters_charactergroups
+ LEFT JOIN cache_charactergroups ON cache_charactergroups.charactergroup_id = characters_charactergroups.charactergroup_id
+ WHERE characters_charactergroups.character_id = CHARACTERID;
+ SET TOTALXPS = QUESTXPS + GROUPSXPS;
+
+
+ SELECT (
+ SELECT id
+ FROM xplevels
+ WHERE seminary_id = charactertypes.seminary_id AND xps = (
+ SELECT MAX(xplevels_sub.xps)
+ FROM xplevels AS xplevels_sub
+ WHERE xplevels_sub.seminary_id = charactertypes.seminary_id AND xps <= TOTALXPS
+ )
+ ) INTO XPLEVELID
+ FROM characters
+ LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id
+ WHERE characters.id = CHARACTERID;
+
+
+ SELECT avatars.id INTO AVATARID
+ FROM characters
+ LEFT JOIN avatars ON avatars.charactertype_id = characters.charactertype_id AND avatars.xplevel_id = XPLEVELID
+ WHERE characters.id = CHARACTERID;
+
+
+ REPLACE INTO cache_characters
+ (character_id, xps, quest_xps, xplevel_id, avatar_id)
+ VALUES
+ (CHARACTERID, TOTALXPS, QUESTXPS, XPLEVELID, AVATARID);
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `update_cache_characters_charactergroup` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`z`@`%` PROCEDURE `update_cache_characters_charactergroup`(
+ IN CHARACTERGROUPID INT(11)
+)
+BEGIN
+ DECLARE CHARACTERID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE characters_cursor CURSOR FOR
+ SELECT character_id
+ FROM characters_charactergroups
+ WHERE charactergroup_id = CHARACTERGROUPID;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ SET done = 0;
+ OPEN characters_cursor;
+ get_characters: LOOP
+ FETCH characters_cursor INTO CHARACTERID;
+ IF done = TRUE THEN
+ LEAVE get_characters;
+ END IF;
+ CALL update_cache_characters(CHARACTERID);
+ END LOOP get_characters;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `update_cache_characters_quest` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`z`@`%` PROCEDURE `update_cache_characters_quest`(
+ IN QUESTID INT(11)
+)
+BEGIN
+ DECLARE CHARACTERID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE characters_cursor CURSOR FOR
+ SELECT character_id
+ FROM quests_characters
+ WHERE quest_id = QUESTID AND status = 3;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ SET done = 0;
+ OPEN characters_cursor;
+ get_characters: LOOP
+ FETCH characters_cursor INTO CHARACTERID;
+ IF done = TRUE THEN
+ LEAVE get_characters;
+ END IF;
+ CALL update_cache_characters(CHARACTERID);
+ END LOOP get_characters;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `update_cache_characters_xplevels` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`z`@`%` PROCEDURE `update_cache_characters_xplevels`(
+ IN SEMINARYID INT(11),
+ IN XPS_LOWER INT(11),
+ IN XPS_UPPER INT(11)
+)
+BEGIN
+ DECLARE CHARACTERID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE characters_cursor CURSOR FOR
+ SELECT cache_characters.character_id
+ FROM charactertypes
+ INNER JOIN characters ON characters.charactertype_id = charactertypes.id
+ INNER JOIN cache_characters ON cache_characters.character_id = characters.id
+ WHERE charactertypes.seminary_id = SEMINARY_ID AND cache_characters.xps >= LEAST(XPS_LOWER, IFNULL(XPS_UPPER,XPS_LOWER)) AND (XPS_UPPER IS NULL OR cache_characters.xps <= GREATEST(XPS_LOWER, XPS_UPPER));
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ IF XPS_UPPER IS NULL THEN
+ SELECT MIN(xps) INTO XPS_UPPER
+ FROM xplevels
+ WHERE seminary_id = SEMINARYID AND xps > XPS_LOWER;
+ END IF;
+
+
+ SET done = 0;
+ OPEN characters_cursor;
+ get_characters: LOOP
+ FETCH characters_cursor INTO CHARACTERID;
+ IF done = TRUE THEN
+ LEAVE get_characters;
+ END IF;
+ CALL update_cache_characters(CHARACTERID);
+ END LOOP get_characters;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2015-10-30 22:27:53
diff --git a/db/drop.sql b/db/drop.sql
new file mode 100644
index 00000000..d5385e5b
--- /dev/null
+++ b/db/drop.sql
@@ -0,0 +1,24 @@
+/** Delete all triggers **/
+DROP TRIGGER IF EXISTS update_cache_characters_insert;
+DROP TRIGGER IF EXISTS update_cache_quests_update;
+DROP TRIGGER IF EXISTS update_cache_quests_characters_insert;
+DROP TRIGGER IF EXISTS update_cache_quests_characters_update;
+DROP TRIGGER IF EXISTS update_cache_quests_characters_delete;
+DROP TRIGGER IF EXISTS update_cache_charactergroups_insert;
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_update;
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_groups_insert;
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_groups_update;
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_groups_delete;
+DROP TRIGGER IF EXISTS update_cache_characters_charactergroups_insert;
+DROP TRIGGER IF EXISTS update_cache_characters_charactergroups_update;
+DROP TRIGGER IF EXISTS update_cache_characters_charactergroups_delete;
+DROP TRIGGER IF EXISTS update_cache_cache_charactergroups_insert;
+DROP TRIGGER IF EXISTS update_cache_cache_charactergroups_update;
+
+
+/** Delete all routines **/
+DROP PROCEDURE IF EXISTS update_cache_characters_quest;
+DROP PROCEDURE IF EXISTS update_cache_characters_charactergroup;
+DROP PROCEDURE IF EXISTS update_cache_characters;
+DROP PROCEDURE IF EXISTS update_cache_charactergroups_charactergroupsquest;
+DROP PROCEDURE IF EXISTS update_cache_charactergroups;
diff --git a/db/import.sql b/db/import.sql
new file mode 100644
index 00000000..da3199f6
--- /dev/null
+++ b/db/import.sql
@@ -0,0 +1,717 @@
+-- MySQL dump 10.15 Distrib 10.0.17-MariaDB, for Linux (x86_64)
+--
+-- Host: localhost Database: z_default
+-- ------------------------------------------------------
+-- Server version 10.0.17-MariaDB-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Dumping data for table `achievementconditions`
+--
+
+LOCK TABLES `achievementconditions` WRITE;
+/*!40000 ALTER TABLE `achievementconditions` DISABLE KEYS */;
+INSERT INTO `achievementconditions` VALUES (1,'2014-04-16 19:36:54','date'),(2,'2014-04-16 19:36:54','character'),(3,'2014-04-16 19:36:59','quest'),(4,'2014-04-16 19:36:59','achievement');
+/*!40000 ALTER TABLE `achievementconditions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `achievementconditions_achievement`
+--
+
+LOCK TABLES `achievementconditions_achievement` WRITE;
+/*!40000 ALTER TABLE `achievementconditions_achievement` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievementconditions_achievement` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `achievementconditions_character`
+--
+
+LOCK TABLES `achievementconditions_character` WRITE;
+/*!40000 ALTER TABLE `achievementconditions_character` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievementconditions_character` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `achievementconditions_date`
+--
+
+LOCK TABLES `achievementconditions_date` WRITE;
+/*!40000 ALTER TABLE `achievementconditions_date` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievementconditions_date` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `achievementconditions_quest`
+--
+
+LOCK TABLES `achievementconditions_quest` WRITE;
+/*!40000 ALTER TABLE `achievementconditions_quest` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievementconditions_quest` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `achievements`
+--
+
+LOCK TABLES `achievements` WRITE;
+/*!40000 ALTER TABLE `achievements` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievements` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `achievements_characters`
+--
+
+LOCK TABLES `achievements_characters` WRITE;
+/*!40000 ALTER TABLE `achievements_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievements_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `achievementsmedia`
+--
+
+LOCK TABLES `achievementsmedia` WRITE;
+/*!40000 ALTER TABLE `achievementsmedia` DISABLE KEYS */;
+/*!40000 ALTER TABLE `achievementsmedia` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `avatarpictures`
+--
+
+LOCK TABLES `avatarpictures` WRITE;
+/*!40000 ALTER TABLE `avatarpictures` DISABLE KEYS */;
+/*!40000 ALTER TABLE `avatarpictures` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `avatars`
+--
+
+LOCK TABLES `avatars` WRITE;
+/*!40000 ALTER TABLE `avatars` DISABLE KEYS */;
+/*!40000 ALTER TABLE `avatars` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `cache_charactergroups`
+--
+
+LOCK TABLES `cache_charactergroups` WRITE;
+/*!40000 ALTER TABLE `cache_charactergroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `cache_charactergroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `cache_characters`
+--
+
+LOCK TABLES `cache_characters` WRITE;
+/*!40000 ALTER TABLE `cache_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `cache_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactergroups`
+--
+
+LOCK TABLES `charactergroups` WRITE;
+/*!40000 ALTER TABLE `charactergroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactergroupsgroups`
+--
+
+LOCK TABLES `charactergroupsgroups` WRITE;
+/*!40000 ALTER TABLE `charactergroupsgroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroupsgroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactergroupsmedia`
+--
+
+LOCK TABLES `charactergroupsmedia` WRITE;
+/*!40000 ALTER TABLE `charactergroupsmedia` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroupsmedia` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactergroupsquests`
+--
+
+LOCK TABLES `charactergroupsquests` WRITE;
+/*!40000 ALTER TABLE `charactergroupsquests` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroupsquests` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactergroupsquests_groups`
+--
+
+LOCK TABLES `charactergroupsquests_groups` WRITE;
+/*!40000 ALTER TABLE `charactergroupsquests_groups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroupsquests_groups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactergroupsquests_seminaryuploads`
+--
+
+LOCK TABLES `charactergroupsquests_seminaryuploads` WRITE;
+/*!40000 ALTER TABLE `charactergroupsquests_seminaryuploads` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroupsquests_seminaryuploads` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `characterroles`
+--
+
+LOCK TABLES `characterroles` WRITE;
+/*!40000 ALTER TABLE `characterroles` DISABLE KEYS */;
+INSERT INTO `characterroles` VALUES (1,'2014-04-16 14:42:54','admin'),(2,'2014-04-16 14:42:54','moderator'),(3,'2014-04-16 14:43:00','user'),(4,'2014-04-16 14:43:00','guest');
+/*!40000 ALTER TABLE `characterroles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `characters`
+--
+
+LOCK TABLES `characters` WRITE;
+/*!40000 ALTER TABLE `characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `characters_charactergroups`
+--
+
+LOCK TABLES `characters_charactergroups` WRITE;
+/*!40000 ALTER TABLE `characters_charactergroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `characters_charactergroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `characters_characterroles`
+--
+
+LOCK TABLES `characters_characterroles` WRITE;
+/*!40000 ALTER TABLE `characters_characterroles` DISABLE KEYS */;
+/*!40000 ALTER TABLE `characters_characterroles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `characters_seminarycharacterfields`
+--
+
+LOCK TABLES `characters_seminarycharacterfields` WRITE;
+/*!40000 ALTER TABLE `characters_seminarycharacterfields` DISABLE KEYS */;
+/*!40000 ALTER TABLE `characters_seminarycharacterfields` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactertypes`
+--
+
+LOCK TABLES `charactertypes` WRITE;
+/*!40000 ALTER TABLE `charactertypes` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactertypes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `maps`
+--
+
+LOCK TABLES `maps` WRITE;
+/*!40000 ALTER TABLE `maps` DISABLE KEYS */;
+/*!40000 ALTER TABLE `maps` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `media`
+--
+
+LOCK TABLES `media` WRITE;
+/*!40000 ALTER TABLE `media` DISABLE KEYS */;
+/*!40000 ALTER TABLE `media` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questgroups`
+--
+
+LOCK TABLES `questgroups` WRITE;
+/*!40000 ALTER TABLE `questgroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questgroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questgroups_characters`
+--
+
+LOCK TABLES `questgroups_characters` WRITE;
+/*!40000 ALTER TABLE `questgroups_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questgroups_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questgroups_questgroupshierarchy`
+--
+
+LOCK TABLES `questgroups_questgroupshierarchy` WRITE;
+/*!40000 ALTER TABLE `questgroups_questgroupshierarchy` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questgroups_questgroupshierarchy` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questgroups_questtexts`
+--
+
+LOCK TABLES `questgroups_questtexts` WRITE;
+/*!40000 ALTER TABLE `questgroups_questtexts` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questgroups_questtexts` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questgroupshierarchy`
+--
+
+LOCK TABLES `questgroupshierarchy` WRITE;
+/*!40000 ALTER TABLE `questgroupshierarchy` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questgroupshierarchy` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questgroupspictures`
+--
+
+LOCK TABLES `questgroupspictures` WRITE;
+/*!40000 ALTER TABLE `questgroupspictures` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questgroupspictures` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questgrouptexts`
+--
+
+LOCK TABLES `questgrouptexts` WRITE;
+/*!40000 ALTER TABLE `questgrouptexts` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questgrouptexts` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `quests`
+--
+
+LOCK TABLES `quests` WRITE;
+/*!40000 ALTER TABLE `quests` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quests` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `quests_characters`
+--
+
+LOCK TABLES `quests_characters` WRITE;
+/*!40000 ALTER TABLE `quests_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quests_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `quests_previousquests`
+--
+
+LOCK TABLES `quests_previousquests` WRITE;
+/*!40000 ALTER TABLE `quests_previousquests` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quests_previousquests` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `quests_questsubtopics`
+--
+
+LOCK TABLES `quests_questsubtopics` WRITE;
+/*!40000 ALTER TABLE `quests_questsubtopics` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quests_questsubtopics` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questsmedia`
+--
+
+LOCK TABLES `questsmedia` WRITE;
+/*!40000 ALTER TABLE `questsmedia` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questsmedia` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questsubtopics`
+--
+
+LOCK TABLES `questsubtopics` WRITE;
+/*!40000 ALTER TABLE `questsubtopics` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questsubtopics` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtexts`
+--
+
+LOCK TABLES `questtexts` WRITE;
+/*!40000 ALTER TABLE `questtexts` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtexts` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtexttypes`
+--
+
+LOCK TABLES `questtexttypes` WRITE;
+/*!40000 ALTER TABLE `questtexttypes` DISABLE KEYS */;
+INSERT INTO `questtexttypes` VALUES (1,'2014-04-17 09:24:21','Prolog','Prolog'),(2,'2014-04-17 09:24:21','Hint','Hint'),(3,'2014-04-17 09:24:27','Epilog','Epilog');
+/*!40000 ALTER TABLE `questtexttypes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtopics`
+--
+
+LOCK TABLES `questtopics` WRITE;
+/*!40000 ALTER TABLE `questtopics` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtopics` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes`
+--
+
+LOCK TABLES `questtypes` WRITE;
+/*!40000 ALTER TABLE `questtypes` DISABLE KEYS */;
+INSERT INTO `questtypes` VALUES (1,'2014-04-16 18:44:44','Empty','Empty',NULL),(2,'2014-04-16 18:44:44','Boss Fight','Boss-Fight','bossfight'),(3,'2014-04-16 18:45:19','Choice Input','Choice-Input','choiceinput'),(4,'2014-04-16 18:46:02','Crossword','Crossword','crossword'),(5,'2014-04-16 18:46:02','Drag&Drop','Drag&Drop','dragndrop'),(6,'2014-04-16 18:46:23','Multiple Choice','Multiple-Choice','multiplechoice'),(7,'2014-04-16 18:46:23','Submit','Submit','submit'),(8,'2014-04-16 18:46:43','Text Input','Text-Input','textinput');
+/*!40000 ALTER TABLE `questtypes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_bossfight`
+--
+
+LOCK TABLES `questtypes_bossfight` WRITE;
+/*!40000 ALTER TABLE `questtypes_bossfight` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_bossfight` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_bossfight_stages`
+--
+
+LOCK TABLES `questtypes_bossfight_stages` WRITE;
+/*!40000 ALTER TABLE `questtypes_bossfight_stages` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_bossfight_stages` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_bossfight_stages_characters`
+--
+
+LOCK TABLES `questtypes_bossfight_stages_characters` WRITE;
+/*!40000 ALTER TABLE `questtypes_bossfight_stages_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_bossfight_stages_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_choiceinput`
+--
+
+LOCK TABLES `questtypes_choiceinput` WRITE;
+/*!40000 ALTER TABLE `questtypes_choiceinput` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_choiceinput` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_choiceinput_choices`
+--
+
+LOCK TABLES `questtypes_choiceinput_choices` WRITE;
+/*!40000 ALTER TABLE `questtypes_choiceinput_choices` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_choiceinput_choices` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_choiceinput_lists`
+--
+
+LOCK TABLES `questtypes_choiceinput_lists` WRITE;
+/*!40000 ALTER TABLE `questtypes_choiceinput_lists` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_choiceinput_lists` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_choiceinput_lists_characters`
+--
+
+LOCK TABLES `questtypes_choiceinput_lists_characters` WRITE;
+/*!40000 ALTER TABLE `questtypes_choiceinput_lists_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_choiceinput_lists_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_crossword_words`
+--
+
+LOCK TABLES `questtypes_crossword_words` WRITE;
+/*!40000 ALTER TABLE `questtypes_crossword_words` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_crossword_words` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_crossword_words_characters`
+--
+
+LOCK TABLES `questtypes_crossword_words_characters` WRITE;
+/*!40000 ALTER TABLE `questtypes_crossword_words_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_crossword_words_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_dragndrop`
+--
+
+LOCK TABLES `questtypes_dragndrop` WRITE;
+/*!40000 ALTER TABLE `questtypes_dragndrop` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_dragndrop` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_dragndrop_drags`
+--
+
+LOCK TABLES `questtypes_dragndrop_drags` WRITE;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drags` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drags` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_dragndrop_drops`
+--
+
+LOCK TABLES `questtypes_dragndrop_drops` WRITE;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drops` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drops` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_dragndrop_drops_characters`
+--
+
+LOCK TABLES `questtypes_dragndrop_drops_characters` WRITE;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drops_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drops_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_dragndrop_drops_drags`
+--
+
+LOCK TABLES `questtypes_dragndrop_drops_drags` WRITE;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drops_drags` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_dragndrop_drops_drags` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_multiplechoice`
+--
+
+LOCK TABLES `questtypes_multiplechoice` WRITE;
+/*!40000 ALTER TABLE `questtypes_multiplechoice` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_multiplechoice` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_multiplechoice_answers`
+--
+
+LOCK TABLES `questtypes_multiplechoice_answers` WRITE;
+/*!40000 ALTER TABLE `questtypes_multiplechoice_answers` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_multiplechoice_answers` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_multiplechoice_characters`
+--
+
+LOCK TABLES `questtypes_multiplechoice_characters` WRITE;
+/*!40000 ALTER TABLE `questtypes_multiplechoice_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_multiplechoice_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_submit_characters`
+--
+
+LOCK TABLES `questtypes_submit_characters` WRITE;
+/*!40000 ALTER TABLE `questtypes_submit_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_submit_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_submit_characters_comments`
+--
+
+LOCK TABLES `questtypes_submit_characters_comments` WRITE;
+/*!40000 ALTER TABLE `questtypes_submit_characters_comments` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_submit_characters_comments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_submit_mimetypes`
+--
+
+LOCK TABLES `questtypes_submit_mimetypes` WRITE;
+/*!40000 ALTER TABLE `questtypes_submit_mimetypes` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_submit_mimetypes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_textinput`
+--
+
+LOCK TABLES `questtypes_textinput` WRITE;
+/*!40000 ALTER TABLE `questtypes_textinput` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_textinput` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_textinput_fields`
+--
+
+LOCK TABLES `questtypes_textinput_fields` WRITE;
+/*!40000 ALTER TABLE `questtypes_textinput_fields` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_textinput_fields` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_textinput_fields_characters`
+--
+
+LOCK TABLES `questtypes_textinput_fields_characters` WRITE;
+/*!40000 ALTER TABLE `questtypes_textinput_fields_characters` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_textinput_fields_characters` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `questtypes_textinput_fieldsizes`
+--
+
+LOCK TABLES `questtypes_textinput_fieldsizes` WRITE;
+/*!40000 ALTER TABLE `questtypes_textinput_fieldsizes` DISABLE KEYS */;
+/*!40000 ALTER TABLE `questtypes_textinput_fieldsizes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `seminaries`
+--
+
+LOCK TABLES `seminaries` WRITE;
+/*!40000 ALTER TABLE `seminaries` DISABLE KEYS */;
+/*!40000 ALTER TABLE `seminaries` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `seminarycharacterfields`
+--
+
+LOCK TABLES `seminarycharacterfields` WRITE;
+/*!40000 ALTER TABLE `seminarycharacterfields` DISABLE KEYS */;
+/*!40000 ALTER TABLE `seminarycharacterfields` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `seminarycharacterfieldtypes`
+--
+
+LOCK TABLES `seminarycharacterfieldtypes` WRITE;
+/*!40000 ALTER TABLE `seminarycharacterfieldtypes` DISABLE KEYS */;
+INSERT INTO `seminarycharacterfieldtypes` VALUES (1,'2014-04-16 18:50:16','Number','Number'),(2,'2014-04-16 18:50:16','Varchar','Varchar'),(3,'2014-04-16 18:50:24','Text','Text'),(4,'2014-04-16 18:50:24','List','List');
+/*!40000 ALTER TABLE `seminarycharacterfieldtypes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `seminarymedia`
+--
+
+LOCK TABLES `seminarymedia` WRITE;
+/*!40000 ALTER TABLE `seminarymedia` DISABLE KEYS */;
+/*!40000 ALTER TABLE `seminarymedia` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `seminaryuploads`
+--
+
+LOCK TABLES `seminaryuploads` WRITE;
+/*!40000 ALTER TABLE `seminaryuploads` DISABLE KEYS */;
+/*!40000 ALTER TABLE `seminaryuploads` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `userroles`
+--
+
+LOCK TABLES `userroles` WRITE;
+/*!40000 ALTER TABLE `userroles` DISABLE KEYS */;
+INSERT INTO `userroles` VALUES (1,'2014-04-16 14:42:54','admin'),(2,'2014-04-16 14:42:54','moderator'),(3,'2014-04-16 14:43:00','user'),(4,'2014-04-16 14:43:00','guest');
+/*!40000 ALTER TABLE `userroles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `users`
+--
+
+LOCK TABLES `users` WRITE;
+/*!40000 ALTER TABLE `users` DISABLE KEYS */;
+INSERT INTO `users` VALUES (1,'2015-04-26 11:24:04','admin','admin','Admin','Admin','','$2y$10$1zCozXcIGak552mkv/K3vOPddrisvPlokJvUjHtHj6VBBRcmznXCG',1);
+/*!40000 ALTER TABLE `users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `users_userroles`
+--
+
+LOCK TABLES `users_userroles` WRITE;
+/*!40000 ALTER TABLE `users_userroles` DISABLE KEYS */;
+INSERT INTO `users_userroles` VALUES (1,1,'2015-04-26 11:33:36');
+/*!40000 ALTER TABLE `users_userroles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `xplevels`
+--
+
+LOCK TABLES `xplevels` WRITE;
+/*!40000 ALTER TABLE `xplevels` DISABLE KEYS */;
+/*!40000 ALTER TABLE `xplevels` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2015-04-26 13:34:20
diff --git a/db/procedures.sql b/db/procedures.sql
new file mode 100644
index 00000000..7ac2fae8
--- /dev/null
+++ b/db/procedures.sql
@@ -0,0 +1,189 @@
+DROP PROCEDURE IF EXISTS update_cache_characters_quest;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' PROCEDURE update_cache_characters_quest(
+ IN QUESTID INT(11)
+)
+BEGIN
+ DECLARE CHARACTERID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE characters_cursor CURSOR FOR
+ SELECT character_id
+ FROM quests_characters
+ WHERE quest_id = QUESTID AND status = 3;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ SET done = 0;
+ OPEN characters_cursor;
+ get_characters: LOOP
+ FETCH characters_cursor INTO CHARACTERID;
+ IF done = TRUE THEN
+ LEAVE get_characters;
+ END IF;
+ CALL update_cache_characters(CHARACTERID);
+ END LOOP get_characters;
+END $$
+DELIMITER ;
+
+
+DROP PROCEDURE IF EXISTS update_cache_characters_charactergroup;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' PROCEDURE update_cache_characters_charactergroup(
+ IN CHARACTERGROUPID INT(11)
+)
+BEGIN
+ DECLARE CHARACTERID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE characters_cursor CURSOR FOR
+ SELECT character_id
+ FROM characters_charactergroups
+ WHERE charactergroup_id = CHARACTERGROUPID;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ SET done = 0;
+ OPEN characters_cursor;
+ get_characters: LOOP
+ FETCH characters_cursor INTO CHARACTERID;
+ IF done = TRUE THEN
+ LEAVE get_characters;
+ END IF;
+ CALL update_cache_characters(CHARACTERID);
+ END LOOP get_characters;
+END $$
+DELIMITER ;
+
+
+DROP PROCEDURE IF EXISTS update_cache_characters_xplevels;
+DELIMITER $$
+CREATE PROCEDURE update_cache_characters_xplevels(
+ IN SEMINARYID INT(11),
+ IN XPS_LOWER INT(11),
+ IN XPS_UPPER INT(11)
+)
+BEGIN
+ DECLARE CHARACTERID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE characters_cursor CURSOR FOR
+ SELECT cache_characters.character_id
+ FROM charactertypes
+ INNER JOIN characters ON characters.charactertype_id = charactertypes.id
+ INNER JOIN cache_characters ON cache_characters.character_id = characters.id
+ WHERE charactertypes.seminary_id = SEMINARY_ID AND cache_characters.xps >= LEAST(XPS_LOWER, IFNULL(XPS_UPPER,XPS_LOWER)) AND (XPS_UPPER IS NULL OR cache_characters.xps <= GREATEST(XPS_LOWER, XPS_UPPER));
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ -- Get upper XPs
+ IF XPS_UPPER IS NULL THEN
+ SELECT MIN(xps) INTO XPS_UPPER
+ FROM xplevels
+ WHERE seminary_id = SEMINARYID AND xps > XPS_LOWER;
+ END IF;
+
+ SET done = 0;
+ OPEN characters_cursor;
+ get_characters: LOOP
+ FETCH characters_cursor INTO CHARACTERID;
+ IF done = TRUE THEN
+ LEAVE get_characters;
+ END IF;
+ CALL update_cache_characters(CHARACTERID);
+ END LOOP get_characters;
+END $$
+DELIMITER ;
+
+
+DROP PROCEDURE IF EXISTS update_cache_characters;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' PROCEDURE update_cache_characters(
+ IN CHARACTERID INT(11)
+)
+BEGIN
+ DECLARE QUESTXPS INT UNSIGNED;
+ DECLARE GROUPSXPS INT UNSIGNED;
+ DECLARE TOTALXPS INT UNSIGNED;
+ DECLARE XPLEVELID INT;
+ DECLARE AVATARID INT;
+
+ /* XPs */
+ SELECT IFNULL(SUM(quests.xps),0) INTO QUESTXPS
+ FROM quests_characters
+ LEFT JOIN quests ON quests.id = quests_characters.quest_id
+ WHERE quests_characters.character_id = CHARACTERID AND quests_characters.status = 3;
+ SELECT IFNULL(SUM(cache_charactergroups.xps),0) INTO GROUPSXPS
+ FROM characters_charactergroups
+ LEFT JOIN cache_charactergroups ON cache_charactergroups.charactergroup_id = characters_charactergroups.charactergroup_id
+ WHERE characters_charactergroups.character_id = CHARACTERID;
+ SET TOTALXPS = QUESTXPS + GROUPSXPS;
+
+ /* XP-level */
+ SELECT (
+ SELECT id
+ FROM xplevels
+ WHERE seminary_id = charactertypes.seminary_id AND xps = (
+ SELECT MAX(xplevels_sub.xps)
+ FROM xplevels AS xplevels_sub
+ WHERE xplevels_sub.seminary_id = charactertypes.seminary_id AND xps <= TOTALXPS
+ )
+ ) INTO XPLEVELID
+ FROM characters
+ LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id
+ WHERE characters.id = CHARACTERID;
+
+ /* Avatar */
+ SELECT avatars.id INTO AVATARID
+ FROM characters
+ LEFT JOIN avatars ON avatars.charactertype_id = characters.charactertype_id AND avatars.xplevel_id = XPLEVELID
+ WHERE characters.id = CHARACTERID;
+
+ /* Set values */
+ REPLACE INTO cache_characters
+ (character_id, xps, quest_xps, xplevel_id, avatar_id)
+ VALUES
+ (CHARACTERID, TOTALXPS, QUESTXPS, XPLEVELID, AVATARID);
+END $$
+DELIMITER ;
+
+
+
+
+DROP PROCEDURE IF EXISTS update_cache_charactergroups_charactergroupsquest;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' PROCEDURE update_cache_charactergroups_charactergroupsquest(
+ IN CHARACTERGROUPSQUESTID INT(11)
+)
+BEGIN
+ DECLARE CHARACTERGROUPID INT;
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE charactergroups_cursor CURSOR FOR
+ SELECT charactergroup_id
+ FROM charactergroupsquests_groups
+ WHERE charactergroupsquest_id = CHARACTERGROUPSQUESTID;
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ SET done = 0;
+ OPEN charactergroups_cursor;
+ get_charactergroups: LOOP
+ FETCH charactergroups_cursor INTO CHARACTERGROUPID;
+ IF done = TRUE THEN
+ LEAVE get_charactergroups;
+ END IF;
+ CALL update_cache_charactergroups(CHARACTERGROUPID);
+ END LOOP get_charactergroups;
+END $$
+DELIMITER ;
+
+
+DROP PROCEDURE IF EXISTS update_cache_charactergroups;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' PROCEDURE update_cache_charactergroups(
+ IN CHARACTERGROUPID INT(11)
+)
+BEGIN
+ /* XPs */
+ REPLACE INTO cache_charactergroups
+ (charactergroup_id, xps)
+ SELECT charactergroups.id, IFNULL(SUM(ROUND(charactergroupsquests.xps * charactergroupsquests_groups.xps_factor)),0) AS xps
+ FROM charactergroups
+ LEFT JOIN charactergroupsquests_groups ON charactergroupsquests_groups.charactergroup_id = charactergroups.id
+ LEFT JOIN charactergroupsquests ON charactergroupsquests.id = charactergroupsquests_groups.charactergroupsquest_id
+ WHERE charactergroups.id = CHARACTERGROUPID;
+END $$
+DELIMITER ;
diff --git a/db/triggers.sql b/db/triggers.sql
new file mode 100644
index 00000000..3ff9e1db
--- /dev/null
+++ b/db/triggers.sql
@@ -0,0 +1,186 @@
+/** Characters + Quests **/
+
+DROP TRIGGER IF EXISTS update_cache_characters_insert;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_characters_insert`
+AFTER INSERT ON characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(NEW.id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_quests_update;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_quests_update`
+AFTER UPDATE ON quests
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_quest(NEW.id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_quests_characters_insert;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_quests_characters_insert`
+AFTER INSERT ON quests_characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(NEW.character_id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_quests_characters_update;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_quests_characters_update`
+AFTER UPDATE ON quests_characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+ CALL update_cache_characters(NEW.character_id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_quests_characters_delete;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_quests_characters_delete`
+AFTER DELETE ON quests_characters
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+END $$
+DELIMITER ;
+
+
+
+
+/** Charactergroups + Charactergroupsquests **/
+
+DROP TRIGGER IF EXISTS update_cache_charactergroups_insert;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_charactergroups_insert`
+AFTER INSERT ON charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(NEW.id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_update;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_charactergroupsquests_update`
+AFTER UPDATE ON charactergroupsquests
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups_charactergroupsquest(NEW.id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_groups_insert;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_charactergroupsquests_groups_insert`
+AFTER INSERT ON charactergroupsquests_groups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(NEW.charactergroup_id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_groups_update;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_charactergroupsquests_groups_update`
+AFTER UPDATE ON charactergroupsquests_groups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(OLD.charactergroup_id);
+ CALL update_cache_charactergroups(NEW.charactergroup_id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_charactergroupsquests_groups_delete;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_charactergroupsquests_groups_delete`
+AFTER DELETE ON charactergroupsquests_groups
+FOR EACH ROW BEGIN
+ CALL update_cache_charactergroups(OLD.charactergroup_id);
+END $$
+DELIMITER ;
+
+
+
+
+/** Characters + Charactergroups **/
+
+DROP TRIGGER IF EXISTS update_cache_characters_charactergroups_insert;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_characters_charactergroups_insert`
+AFTER INSERT ON characters_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(NEW.character_id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_characters_charactergroups_update;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_characters_charactergroups_update`
+AFTER UPDATE ON characters_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+ CALL update_cache_characters(NEW.character_id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_characters_charactergroups_delete;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_characters_charactergroups_delete`
+AFTER DELETE ON characters_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters(OLD.character_id);
+END $$
+DELIMITER ;
+
+
+
+
+/** Cache Charactergroups **/
+
+DROP TRIGGER IF EXISTS update_cache_cache_charactergroups_insert;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_cache_charactergroups_insert`
+AFTER INSERT ON cache_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_charactergroup(NEW.charactergroup_id);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_cache_charactergroups_update;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_cache_charactergroups_update`
+AFTER UPDATE ON cache_charactergroups
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_charactergroup(NEW.charactergroup_id);
+END $$
+DELIMITER ;
+
+
+
+
+/** XP-levels **/
+
+DROP TRIGGER IF EXISTS update_cache_characters_xplevels_insert;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_characters_xplevels_insert`
+AFTER INSERT ON xplevels
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_xplevels(NEW.seminary_id, NEW.xps, NULL);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_characters_xplevels_update;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_characters_xplevels_update`
+AFTER UPDATE ON xplevels
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_xplevels(NEW.seminary_id, OLD.xps, NEW.xps);
+END $$
+DELIMITER ;
+
+DROP TRIGGER IF EXISTS update_cache_characters_xplevels_delete;
+DELIMITER $$
+CREATE DEFINER = 'z'@'%' TRIGGER `update_cache_characters_xplevels_delete`
+AFTER DELETE ON xplevels
+FOR EACH ROW BEGIN
+ CALL update_cache_characters_xplevels(OLD.seminary_id, OLD.xps, NULL);
+END $$
+DELIMITER ;
diff --git a/doc/.htaccess b/doc/.htaccess
new file mode 100644
index 00000000..7b01f9bf
--- /dev/null
+++ b/doc/.htaccess
@@ -0,0 +1,5 @@
+# Fixes a vulnerability in CentOS: http://stackoverflow.com/questions/20533279/prevent-php-from-parsing-non-php-files-such-as-somefile-php-txt
+
+ RemoveHandler .php
+ ForceType text/plain
+
\ No newline at end of file
diff --git a/doc/classes/PHPMailer.html b/doc/classes/PHPMailer.html
new file mode 100644
index 00000000..b5d80bfc
--- /dev/null
+++ b/doc/classes/PHPMailer.html
@@ -0,0 +1,7052 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \
+
+
+
+
\ PHPMailer
+
PHPMailer - PHP email creation and transport class.
+
+
+
+
+ Summary
+
+
+
+
+
+
+ No private properties found
+
+
+
+
+
+
+
+
+
+ File
+ app/lib/PHPMailer.inc
+ Package
+ PHPMailer
+ Class hierarchy
+
+ \PHPMailer
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+ STOP_MESSAGE
+ STOP_MESSAGE
+ Error severity: message only, continue processing
+
+
+
+
+
+
+
+
+
+
+ STOP_CONTINUE
+ STOP_CONTINUE
+ Error severity: message, likely ok to continue processing
+
+
+
+
+
+
+
+
+
+
+ STOP_CRITICAL
+ STOP_CRITICAL
+ Error severity: message, plus full stop, critical error reached
+
+
+
+
+
+
+
+
+
+
+ CRLF
+ CRLF
+ SMTP RFC standard line ending
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $Version
+ $Version :
+ The PHPMailer Version number.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Priority
+ $Priority :
+ Email priority.
+ Options: 1 = High, 3 = Normal, 5 = low.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ $CharSet
+ $CharSet :
+ The character set of the message.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $ContentType
+ $ContentType :
+ The MIME Content-type of the message.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Encoding
+ $Encoding :
+ The message encoding.
+ Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $ErrorInfo
+ $ErrorInfo :
+ Holds the most recent mailer error message.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $From
+ $From :
+ The From email address for the message.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $FromName
+ $FromName :
+ The From name of the message.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Sender
+ $Sender :
+ The Sender email (Return-Path) of the message.
+ If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $ReturnPath
+ $ReturnPath :
+ The Return-Path of the message.
+ If empty, it will be set to either From or Sender.
+
+ Type
+
+
+
+
+
+
+ Deprecated
+ Email senders should never set a return-path header;
+it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
+
+
+ See also
+ RFC5321 reference
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Subject
+ $Subject :
+ The Subject of the message.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Body
+ $Body :
+ An HTML or plain text message body.
+ If HTML then call isHTML(true).
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $AltBody
+ $AltBody :
+ The plain-text message body.
+ This body can be read by mail clients that do not have HTML email
+capability such as mutt & Eudora.
+Clients that can read HTML will view the normal Body.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Ical
+ $Ical :
+ An iCal message part body.
+ Only supported in simple alt or alt_inline message types
+To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
+
+ Type
+
+
+
+
+
+
+
+
+
+
+ $WordWrap
+ $WordWrap :
+ Word-wrap the message body to this number of chars.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ $Mailer
+ $Mailer :
+ Which method to use to send mail.
+ Options: "mail", "sendmail", or "smtp".
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Sendmail
+ $Sendmail :
+ The path to the sendmail program.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $UseSendmailOptions
+ $UseSendmailOptions :
+ Whether mail() uses a fully sendmail-compatible MTA.
+ One which supports sendmail's "-oi -f" options.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ bool
+
+
+
+
+
+
+
+
+
+
+ $PluginDir
+ $PluginDir :
+ Path to PHPMailer plugins.
+ Useful if the SMTP class is not in the PHP include path.
+
+ Type
+
+
+
+
+
+
+ Deprecated
+ Should not be needed now there is an autoloader.
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $ConfirmReadingTo
+ $ConfirmReadingTo :
+ The email address that a reading confirmation should be sent to.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Hostname
+ $Hostname :
+ The hostname to use in Message-Id and Received headers
+and as default HELO string.
+ If empty, the value returned
+by SERVER_NAME is used or 'localhost.localdomain'.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $MessageID
+ $MessageID :
+ An ID to be used in the Message-Id header.
+ If empty, a unique id will be generated.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $MessageDate
+ $MessageDate :
+ The message Date to be used in the Date header.
+ If empty, the current date will be added.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Host
+ $Host :
+ SMTP hosts.
+ Either a single hostname or multiple semicolon-delimited hostnames.
+You can also specify a different port
+for each host by using this format: [hostname:port]
+(e.g. "smtp1.example.com:25;smtp2.example.com").
+Hosts will be tried in order.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Port
+ $Port :
+ The default SMTP server port.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+ Todo
+
+
+ Why is this needed when the SMTP class takes care of it?
+
+
+
+
+
+
+
+
+
+
+ $Helo
+ $Helo :
+ The SMTP HELO of the message.
+ Default is $Hostname.
+
+ Type
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $SMTPSecure
+ $SMTPSecure :
+ The secure connection prefix.
+ Options: "", "ssl" or "tls"
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $SMTPAuth
+ $SMTPAuth :
+ Whether to use SMTP authentication.
+ Uses the Username and Password properties.
+
+ Type
+
+
+
+
+
+
+ See also
+
+
+
+ Tags
+
+
+
+ type
+
+
+ bool
+
+
+
+
+
+
+
+
+
+
+ $Username
+ $Username :
+ SMTP username.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Password
+ $Password :
+ SMTP password.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $AuthType
+ $AuthType :
+ SMTP auth type.
+ Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Realm
+ $Realm :
+ SMTP realm.
+ Used for NTLM auth
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Workstation
+ $Workstation :
+ SMTP workstation.
+ Used for NTLM auth
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $Timeout
+ $Timeout :
+ The SMTP server timeout in seconds.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ $SMTPDebug
+ $SMTPDebug :
+ SMTP class debug output mode.
+ Options:
+0: no output
+1: commands
+2: data and commands
+3: as 2 plus connection status
+4: low level data output
+
+ Type
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ $Debugoutput
+ $Debugoutput :
+ How to handle debug output.
+ Options:
+'echo': Output plain-text as-is, appropriate for CLI
+'html': Output escaped, line breaks converted to , appropriate for browser output
+'error_log': Output to error log as configured in php.ini
+
+ Type
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $SMTPKeepAlive
+ $SMTPKeepAlive :
+ Whether to keep SMTP connection open after each message.
+ If this is set to true then to close the connection
+requires an explicit call to smtpClose().
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ bool
+
+
+
+
+
+
+
+
+
+
+ $SingleTo
+ $SingleTo :
+ Whether to split multiple to addresses into multiple messages
+or send them all in one message.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ bool
+
+
+
+
+
+
+
+
+
+
+ $SingleToArray
+ $SingleToArray :
+ Storage for addresses when SingleTo is enabled.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+ todo
+
+
+ This should really not be public
+
+
+
+
+
+
+
+
+
+
+ $do_verp
+ $do_verp :
+ Whether to generate VERP addresses on send.
+ Only applicable when sending via SMTP.
+
+ Type
+
+
+
+
+
+
+
+
+
+
+ $AllowEmpty
+ $AllowEmpty :
+ Whether to allow sending messages with an empty body.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ bool
+
+
+
+
+
+
+
+
+
+
+ $LE
+ $LE :
+ The default line ending.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ note
+
+
+ The default remains "\n". We force CRLF where we know
+it must be used via self::CRLF.
+
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $DKIM_selector
+ $DKIM_selector :
+ DKIM selector.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $DKIM_identity
+ $DKIM_identity :
+ DKIM Identity.
+ Usually the email address used as the source of the email
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $DKIM_passphrase
+ $DKIM_passphrase :
+ DKIM passphrase.
+ Used if your key is encrypted.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $DKIM_domain
+ $DKIM_domain :
+ DKIM signing domain name.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ example
+
+
+
+
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $DKIM_private
+ $DKIM_private :
+ DKIM private key file path.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $action_function
+ $action_function :
+ Callback Action function name.
+ The function that handles the result of the send email action.
+It is called out by send() for each email sent.
+Value can be any php callable: http://www.php.net/is_callable
+Parameters:
+bool $result result of the send action
+string $to email address of the recipient
+string $cc cc email addresses
+string $bcc bcc email addresses
+string $subject the subject
+string $body the email body
+string $from email address of sender
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $XMailer
+ $XMailer :
+ What to use in the X-Mailer header.
+ Options: null for default, whitespace for none, or a string to use
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $MIMEBody
+ $MIMEBody :
+ The complete compiled MIME message body.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $MIMEHeader
+ $MIMEHeader :
+ The complete compiled MIME message headers.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $mailHeader
+ $mailHeader :
+ Extra headers that createHeader() doesn't fold in.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $smtp
+ $smtp :
+ An instance of the SMTP sender class.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ SMTP
+
+
+
+
+
+
+
+
+
+
+ $to
+ $to :
+ The array of 'to' addresses.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $cc
+ $cc :
+ The array of 'cc' addresses.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $bcc
+ $bcc :
+ The array of 'bcc' addresses.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $ReplyTo
+ $ReplyTo :
+ The array of reply-to names and addresses.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $all_recipients
+ $all_recipients :
+ An array of all kinds of addresses.
+ Includes all of $to, $cc, $bcc, $replyto
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $attachment
+ $attachment :
+ The array of attachments.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $CustomHeader
+ $CustomHeader :
+ The array of custom headers.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $lastMessageID
+ $lastMessageID :
+ The most recent Message-ID (including angular brackets).
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $message_type
+ $message_type :
+ The message's MIME type.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $boundary
+ $boundary :
+ The array of MIME boundary strings.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $language
+ $language :
+ The array of available languages.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ array
+
+
+
+
+
+
+
+
+
+
+ $error_count
+ $error_count :
+ The number of errors encountered.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ integer
+
+
+
+
+
+
+
+
+
+
+ $sign_cert_file
+ $sign_cert_file :
+ The S/MIME certificate file path.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $sign_key_file
+ $sign_key_file :
+ The S/MIME key file path.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $sign_key_pass
+ $sign_key_pass :
+ The S/MIME password for the key.
+ Used only if the key is encrypted.
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $exceptions
+ $exceptions :
+ Whether to throw exceptions for errors.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(boolean $exceptions = false )
+ Constructor
+
+
+ Parameters
+
+
+ boolean
+ $exceptions
+ Should we throw external exceptions?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __destruct()
+
+ __destruct()
+ Destructor.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ isHTML()
+
+ isHTML(boolean $ishtml = true ) : void
+ Sets message type to HTML or plain.
+
+
+ Parameters
+
+
+ boolean
+ $ishtml
+ True for HTML mode.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ isSMTP()
+
+ isSMTP() : void
+ Send messages using SMTP.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ isMail()
+
+ isMail() : void
+ Send messages using PHP's mail() function.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ isSendmail()
+
+ isSendmail() : void
+ Send messages using $Sendmail.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ isQmail()
+
+ isQmail() : void
+ Send messages using qmail.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addAddress()
+
+ addAddress(string $address , string $name = '' ) : boolean
+ Add a "To" address.
+
+
+ Parameters
+
+
+ string
+ $address
+
+
+
+ string
+ $name
+
+
+
+
+
+ Returns
+ boolean
+ — true on success, false if address already used
+
+
+
+
+
+
+
+
+
+
+ addCC()
+
+ addCC(string $address , string $name = '' ) : boolean
+ Add a "CC" address.
+
+
+ Parameters
+
+
+ string
+ $address
+
+
+
+ string
+ $name
+
+
+
+
+
+ Returns
+ boolean
+ — true on success, false if address already used
+
+
+
+
+
+
+
+ Tags
+
+
+
+ note
+
+
+ : This function works with the SMTP mailer on win32, not with the "mail" mailer.
+
+
+
+
+
+
+
+
+
+
+ addBCC()
+
+ addBCC(string $address , string $name = '' ) : boolean
+ Add a "BCC" address.
+
+
+ Parameters
+
+
+ string
+ $address
+
+
+
+ string
+ $name
+
+
+
+
+
+ Returns
+ boolean
+ — true on success, false if address already used
+
+
+
+
+
+
+
+ Tags
+
+
+
+ note
+
+
+ : This function works with the SMTP mailer on win32, not with the "mail" mailer.
+
+
+
+
+
+
+
+
+
+
+ addReplyTo()
+
+ addReplyTo(string $address , string $name = '' ) : boolean
+ Add a "Reply-to" address.
+
+
+ Parameters
+
+
+ string
+ $address
+
+
+
+ string
+ $name
+
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ setFrom()
+
+ setFrom(string $address , string $name = '' , boolean $auto = true ) : boolean
+ Set the From and FromName properties.
+
+
+ Parameters
+
+
+ string
+ $address
+
+
+
+ string
+ $name
+
+
+
+ boolean
+ $auto
+ Whether to also set the Sender address, defaults to true
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ getLastMessageID()
+
+ getLastMessageID() : string
+ Return the Message-ID header of the last email.
+ Technically this is the value from the last time the headers were created,
+but it's also the message ID of the last sent message except in
+pathological cases.
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ validateAddress()
+
+ validateAddress(string $address , string $patternselect = 'auto' ) : boolean
+ Check that a string looks like an email address.
+
+
+ Parameters
+
+
+ string
+ $address
+ The email address to check
+
+
+ string
+ $patternselect
+ A selector for the validation pattern to use :
+
+auto
Pick strictest one automatically;
+pcre8
Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
+pcre
Use old PCRE implementation;
+php
Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
+html5
Use the pattern given by the HTML5 spec for 'email' type form input elements.
+noregex
Don't use a regex: super fast, really dumb.
+
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ send()
+
+ send() : boolean
+ Create a message and send it.
+ Uses the sending method specified by $Mailer.
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+ — false on error - See the ErrorInfo property for details of the error.
+
+
+
+
+
+
+
+
+
+
+ preSend()
+
+ preSend() : boolean
+ Prepare a message for sending.
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ postSend()
+
+ postSend() : boolean
+ Actually send a message.
+ Send the email via the selected mechanism
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ getSMTPInstance()
+
+ getSMTPInstance() : \SMTP
+ Get an instance to use for SMTP operations.
+ Override this function to load your own SMTP implementation
+
+
+
+ Returns
+ \SMTP
+
+
+
+
+
+
+
+
+
+
+ smtpConnect()
+
+ smtpConnect(array $options = array() ) : boolean
+ Initiate a connection to an SMTP server.
+ Returns false if the operation failed.
+
+ Parameters
+
+
+ array
+ $options
+ An array of options compatible with stream_context_create()
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+ Uses
+ SMTP
+
+ Tags
+
+
+
+
+
+
+
+
+ smtpClose()
+
+ smtpClose() : void
+ Close the active SMTP session if one exists.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setLanguage()
+
+ setLanguage(string $langcode = 'en' , string $lang_path = '' ) : boolean
+ Set the language for error messages.
+ Returns false if it cannot load the language file.
+The default language is English.
+
+ Parameters
+
+
+ string
+ $langcode
+ ISO 639-1 2-character language code (e.g. French is "fr")
+
+
+ string
+ $lang_path
+ Path to the language file directory, with trailing separator (slash)
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ getTranslations()
+
+ getTranslations() : array
+ Get the array of strings for the current language.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ addrAppend()
+
+ addrAppend(string $type , array $addr ) : string
+ Create recipient headers.
+
+
+ Parameters
+
+
+ string
+ $type
+
+
+
+ array
+ $addr
+ An array of recipient,
+where each recipient is a 2-element indexed array with element 0 containing an address
+and element 1 containing a name, like:
+array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ addrFormat()
+
+ addrFormat(array $addr ) : string
+ Format an address for use in a message header.
+
+
+ Parameters
+
+
+ array
+ $addr
+ A 2-element indexed array, element 0 containing an address, element 1 containing a name
+like array('joe@example.com', 'Joe User')
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ wrapText()
+
+ wrapText(string $message , integer $length , boolean $qp_mode = false ) : string
+ Word-wrap message.
+ For use with mailers that do not automatically perform wrapping
+and for quoted-printable encoded messages.
+Original written by philippe.
+
+ Parameters
+
+
+ string
+ $message
+ The message to wrap
+
+
+ integer
+ $length
+ The line length to wrap to
+
+
+ boolean
+ $qp_mode
+ Whether to run in Quoted-Printable mode
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ utf8CharBoundary()
+
+ utf8CharBoundary(string $encodedText , integer $maxLength ) : integer
+ Find the last character boundary prior to $maxLength in a utf-8
+quoted (printable) encoded string.
+ Original written by Colin Brown.
+
+ Parameters
+
+
+ string
+ $encodedText
+ utf-8 QP text
+
+
+ integer
+ $maxLength
+ find last character boundary prior to this length
+
+
+
+
+ Returns
+ integer
+
+
+
+
+
+
+
+
+
+
+ setWordWrap()
+
+ setWordWrap() : void
+ Set the body wrapping.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ createHeader()
+
+ createHeader() : string
+ Assemble message headers.
+
+
+
+
+ Returns
+ string
+ — The assembled headers
+
+
+
+
+
+
+
+
+
+
+ getMailMIME()
+
+ getMailMIME() : string
+ Get the message MIME type headers.
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ getSentMIMEMessage()
+
+ getSentMIMEMessage() : string
+ Returns the whole MIME message.
+ Includes complete headers and body.
+Only valid post preSend().
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+
+
+
+
+
+ createBody()
+
+ createBody() : string
+ Assemble the message body.
+ Returns an empty string on failure.
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ string
+ — The assembled message body
+
+
+
+
+
+
+
+
+
+
+ headerLine()
+
+ headerLine(string $name , string $value ) : string
+ Format a header line.
+
+
+ Parameters
+
+
+ string
+ $name
+
+
+
+ string
+ $value
+
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ textLine()
+
+ textLine(string $value ) : string
+ Return a formatted mail line.
+
+
+ Parameters
+
+
+ string
+ $value
+
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ addAttachment()
+
+ addAttachment(string $path , string $name = '' , string $encoding = 'base64' , string $type = '' , string $disposition = 'attachment' ) : boolean
+ Add an attachment from a path on the filesystem.
+ Returns false if the file could not be found or read.
+
+ Parameters
+
+
+ string
+ $path
+ Path to the attachment.
+
+
+ string
+ $name
+ Overrides the attachment name.
+
+
+ string
+ $encoding
+ File encoding (see $Encoding).
+
+
+ string
+ $type
+ File extension (MIME) type.
+
+
+ string
+ $disposition
+ Disposition to use
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ getAttachments()
+
+ getAttachments() : array
+ Return the array of attachments.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ encodeString()
+
+ encodeString(string $str , string $encoding = 'base64' ) : string
+ Encode a string in requested format.
+ Returns an empty string on failure.
+
+ Parameters
+
+
+ string
+ $str
+ The text to encode
+
+
+ string
+ $encoding
+ The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ encodeHeader()
+
+ encodeHeader(string $str , string $position = 'text' ) : string
+ Encode a header string optimally.
+ Picks shortest of Q, B, quoted-printable or none.
+
+ Parameters
+
+
+ string
+ $str
+
+
+
+ string
+ $position
+
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ hasMultiBytes()
+
+ hasMultiBytes(string $str ) : boolean
+ Check if a string contains multi-byte characters.
+
+
+ Parameters
+
+
+ string
+ $str
+ multi-byte text to wrap encode
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ has8bitChars()
+
+ has8bitChars(string $text ) : boolean
+ Does a string contain any 8-bit chars (in any charset)?
+
+
+ Parameters
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ base64EncodeWrapMB()
+
+ base64EncodeWrapMB(string $str , string $linebreak = null ) : string
+ Encode and wrap long multibyte strings for mail headers
+without breaking lines within a character.
+ Adapted from a function by paravoid
+
+ Parameters
+
+
+ string
+ $str
+ multi-byte text to wrap encode
+
+
+ string
+ $linebreak
+ string to use as linefeed/end-of-line
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ encodeQP()
+
+ encodeQP(string $string , integer $line_max = 76 ) : string
+ Encode a string in quoted-printable format.
+ According to RFC2045 section 6.7.
+
+ Parameters
+
+
+ string
+ $string
+ The text to encode
+
+
+ integer
+ $line_max
+ Number of chars allowed on a line before wrapping
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ encodeQPphp()
+
+ encodeQPphp(string $string , integer $line_max = 76 , boolean $space_conv = false ) : string
+ Backward compatibility wrapper for an old QP encoding function that was removed.
+
+
+ Parameters
+
+
+ string
+ $string
+
+
+
+ integer
+ $line_max
+
+
+
+ boolean
+ $space_conv
+
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+ Deprecated
+ Use encodeQP instead.
+
+
+ See also
+
+
+ Tags
+
+
+
+
+
+
+
+
+ encodeQ()
+
+ encodeQ(string $str , string $position = 'text' ) : string
+ Encode a string using Q encoding.
+
+
+ Parameters
+
+
+ string
+ $str
+ the text to encode
+
+
+ string
+ $position
+ Where the text is going to be used, see the RFC for what that means
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ addStringAttachment()
+
+ addStringAttachment(string $string , string $filename , string $encoding = 'base64' , string $type = '' , string $disposition = 'attachment' ) : void
+ Add a string or binary attachment (non-filesystem).
+ This method can be used to attach ascii or binary data,
+such as a BLOB record from a database.
+
+ Parameters
+
+
+ string
+ $string
+ String attachment data.
+
+
+ string
+ $filename
+ Name of the attachment.
+
+
+ string
+ $encoding
+ File encoding (see $Encoding).
+
+
+ string
+ $type
+ File extension (MIME) type.
+
+
+ string
+ $disposition
+ Disposition to use
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addEmbeddedImage()
+
+ addEmbeddedImage(string $path , string $cid , string $name = '' , string $encoding = 'base64' , string $type = '' , string $disposition = 'inline' ) : boolean
+ Add an embedded (inline) attachment from a file.
+ This can include images, sounds, and just about any other document type.
+These differ from 'regular' attachmants in that they are intended to be
+displayed inline with the message, not just attached for download.
+This is used in HTML messages that embed the images
+the HTML refers to using the $cid value.
+
+ Parameters
+
+
+ string
+ $path
+ Path to the attachment.
+
+
+ string
+ $cid
+ Content ID of the attachment; Use this to reference
+the content when using an embedded image in HTML.
+
+
+ string
+ $name
+ Overrides the attachment name.
+
+
+ string
+ $encoding
+ File encoding (see $Encoding).
+
+
+ string
+ $type
+ File MIME type.
+
+
+ string
+ $disposition
+ Disposition to use
+
+
+
+
+ Returns
+ boolean
+ — True on successfully adding an attachment
+
+
+
+
+
+
+
+
+
+
+ addStringEmbeddedImage()
+
+ addStringEmbeddedImage(string $string , string $cid , string $name = '' , string $encoding = 'base64' , string $type = '' , string $disposition = 'inline' ) : boolean
+ Add an embedded stringified attachment.
+ This can include images, sounds, and just about any other document type.
+Be sure to set the $type to an image type for images:
+JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
+
+ Parameters
+
+
+ string
+ $string
+ The attachment binary data.
+
+
+ string
+ $cid
+ Content ID of the attachment; Use this to reference
+the content when using an embedded image in HTML.
+
+
+ string
+ $name
+
+
+
+ string
+ $encoding
+ File encoding (see $Encoding).
+
+
+ string
+ $type
+ MIME type.
+
+
+ string
+ $disposition
+ Disposition to use
+
+
+
+
+ Returns
+ boolean
+ — True on successfully adding an attachment
+
+
+
+
+
+
+
+
+
+
+ inlineImageExists()
+
+ inlineImageExists() : boolean
+ Check if an inline attachment is present.
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ attachmentExists()
+
+ attachmentExists() : boolean
+ Check if an attachment (non-inline) is present.
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ alternativeExists()
+
+ alternativeExists() : boolean
+ Check if this message has an alternative body set.
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ clearAddresses()
+
+ clearAddresses() : void
+ Clear all To recipients.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ clearCCs()
+
+ clearCCs() : void
+ Clear all CC recipients.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ clearBCCs()
+
+ clearBCCs() : void
+ Clear all BCC recipients.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ clearReplyTos()
+
+ clearReplyTos() : void
+ Clear all ReplyTo recipients.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ clearAllRecipients()
+
+ clearAllRecipients() : void
+ Clear all recipient types.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ clearAttachments()
+
+ clearAttachments() : void
+ Clear all filesystem, string, and binary attachments.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ clearCustomHeaders()
+
+ clearCustomHeaders() : void
+ Clear all custom headers.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rfcDate()
+
+ rfcDate() : string
+ Return an RFC 822 formatted date.
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ isError()
+
+ isError() : boolean
+ Check if an error occurred.
+
+
+
+
+ Returns
+ boolean
+ — True if an error did occur.
+
+
+
+
+
+
+
+
+
+
+ fixEOL()
+
+ fixEOL(string $str ) : string
+ Ensure consistent line endings in a string.
+ Changes every end of line from CRLF, CR or LF to $this->LE.
+
+ Parameters
+
+
+ string
+ $str
+ String to fixEOL
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ addCustomHeader()
+
+ addCustomHeader(string $name , string $value = null ) : void
+ Add a custom header.
+ $name value can be overloaded to contain
+both header name and value (name:value)
+
+ Parameters
+
+
+ string
+ $name
+ Custom header name
+
+
+ string
+ $value
+ Header value
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ msgHTML()
+
+ msgHTML(string $message , string $basedir = '' , boolean $advanced = false ) : string
+ Create a message from an HTML string.
+ Automatically makes modifications for inline images and backgrounds
+and creates a plain-text version by converting the HTML.
+Overwrites any existing values in $this->Body and $this->AltBody
+
+ Parameters
+
+
+ string
+ $message
+ HTML message string
+
+
+ string
+ $basedir
+ baseline directory for path
+
+
+ boolean
+ $advanced
+ Whether to use the advanced HTML to text converter
+
+
+
+
+ Returns
+ string
+ — $message
+
+
+
+
+
+
+
+
+
+
+ html2text()
+
+ html2text(string $html , boolean $advanced = false ) : string
+ Convert an HTML string into plain text.
+
+
+ Parameters
+
+
+ string
+ $html
+ The HTML text to convert
+
+
+ boolean
+ $advanced
+ Should this use the more complex html2text converter or just a simple one?
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ _mime_types()
+
+ _mime_types(string $ext = '' ) : string
+ Get the MIME type for a file extension.
+
+
+ Parameters
+
+
+ string
+ $ext
+ File extension
+
+
+
+
+ Returns
+ string
+ — MIME type of file.
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ filenameToType()
+
+ filenameToType(string $filename ) : string
+ Map a file name to a MIME type.
+ Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
+
+ Parameters
+
+
+ string
+ $filename
+ A file name or full path, does not need to exist as a file
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ mb_pathinfo()
+
+ mb_pathinfo(string $path , integer|string $options = null ) : string|array
+ Multi-byte-safe pathinfo replacement.
+ Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
+Works similarly to the one in PHP >= 5.2.0
+
+ Parameters
+
+
+ string
+ $path
+ A filename or path, does not need to exist as a file
+
+
+ integer|string
+ $options
+ Either a PATHINFO_* constant,
+or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
+
+
+
+
+ Returns
+ string|array
+
+
+
+
+
+
+
+
+
+
+ set()
+
+ set(string $name , mixed $value = '' ) : boolean
+ Set or reset instance properties.
+ Usage Example:
+$page->set('X-Priority', '3');
+
+ Parameters
+
+
+ string
+ $name
+
+
+
+ mixed
+ $value
+ NOTE: will not work with arrays, there are no arrays to set/reset
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+ Tags
+
+
+
+ todo
+
+
+ Should this not be using __set() magic function?
+
+
+
+
+
+
+
+
+
+
+ secureHeader()
+
+ secureHeader(string $str ) : string
+ Strip newlines to prevent header injection.
+
+
+ Parameters
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ normalizeBreaks()
+
+ normalizeBreaks(string $text , string $breaktype = "\r\n" ) : string
+ Normalize line breaks in a string.
+ Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
+Defaults to CRLF (for message bodies) and preserves consecutive breaks.
+
+ Parameters
+
+
+ string
+ $text
+
+
+
+ string
+ $breaktype
+ What kind of line break to use, defaults to CRLF
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ sign()
+
+ sign(string $cert_filename , string $key_filename , string $key_pass )
+ Set the public and private key files and password for S/MIME signing.
+
+
+ Parameters
+
+
+ string
+ $cert_filename
+
+
+
+ string
+ $key_filename
+
+
+
+ string
+ $key_pass
+ Password for private key
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DKIM_QP()
+
+ DKIM_QP(string $txt ) : string
+ Quoted-Printable-encode a DKIM header.
+
+
+ Parameters
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ DKIM_Sign()
+
+ DKIM_Sign(string $signheader ) : string
+ Generate a DKIM signature.
+
+
+ Parameters
+
+
+ string
+ $signheader
+ Header
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ DKIM_HeaderC()
+
+ DKIM_HeaderC(string $signheader ) : string
+ Generate a DKIM canonicalization header.
+
+
+ Parameters
+
+
+ string
+ $signheader
+ Header
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ DKIM_BodyC()
+
+ DKIM_BodyC(string $body ) : string
+ Generate a DKIM canonicalization body.
+
+
+ Parameters
+
+
+ string
+ $body
+ Message Body
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ DKIM_Add()
+
+ DKIM_Add(string $headers_line , string $subject , string $body ) : string
+ Create the DKIM header and body in a new message header.
+
+
+ Parameters
+
+
+ string
+ $headers_line
+ Header lines
+
+
+ string
+ $subject
+ Subject
+
+
+ string
+ $body
+ Body
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ getToAddresses()
+
+ getToAddresses() : array
+ Allows for public read access to 'to' property.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ getCcAddresses()
+
+ getCcAddresses() : array
+ Allows for public read access to 'cc' property.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ getBccAddresses()
+
+ getBccAddresses() : array
+ Allows for public read access to 'bcc' property.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ getReplyToAddresses()
+
+ getReplyToAddresses() : array
+ Allows for public read access to 'ReplyTo' property.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ getAllRecipientAddresses()
+
+ getAllRecipientAddresses() : array
+ Allows for public read access to 'all_recipients' property.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ edebug()
+
+ edebug(string $str )
+ Output debugging info via user-defined method.
+ Only if debug output is enabled.
+
+ Parameters
+
+
+
+
+
+
+
+
+
+ See also
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+ addAnAddress()
+
+ addAnAddress(string $kind , string $address , string $name = '' ) : boolean
+ Add an address to one of the recipient arrays.
+ Addresses that have been added already return false, but do not throw exceptions
+
+ Parameters
+
+
+ string
+ $kind
+ One of 'to', 'cc', 'bcc', 'ReplyTo'
+
+
+ string
+ $address
+ The email address to send to
+
+
+ string
+ $name
+
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+ — true on success, false if address already used or invalid in some way
+
+
+
+
+
+
+
+
+
+
+ sendmailSend()
+
+ sendmailSend(string $header , string $body ) : boolean
+ Send mail using the $Sendmail program.
+
+
+ Parameters
+
+
+ string
+ $header
+ The message headers
+
+
+ string
+ $body
+ The message body
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+
+
+
+
+
+ mailSend()
+
+ mailSend(string $header , string $body ) : boolean
+ Send mail using the PHP mail() function.
+
+
+ Parameters
+
+
+ string
+ $header
+ The message headers
+
+
+ string
+ $body
+ The message body
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ smtpSend()
+
+ smtpSend(string $header , string $body ) : boolean
+ Send mail via SMTP.
+ Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
+Uses the PHPMailerSMTP class by default.
+
+ Parameters
+
+
+ string
+ $header
+ The message headers
+
+
+ string
+ $body
+ The message body
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ getBoundary()
+
+ getBoundary(string $boundary , string $charSet , string $contentType , string $encoding ) : string
+ Return the start of a message boundary.
+
+
+ Parameters
+
+
+ string
+ $boundary
+
+
+
+ string
+ $charSet
+
+
+
+ string
+ $contentType
+
+
+
+ string
+ $encoding
+
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ endBoundary()
+
+ endBoundary(string $boundary ) : string
+ Return the end of a message boundary.
+
+
+ Parameters
+
+
+ string
+ $boundary
+
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ setMessageType()
+
+ setMessageType() : void
+ Set the message type.
+ PHPMailer only supports some preset message types,
+not arbitrary MIME structures.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ attachAll()
+
+ attachAll(string $disposition_type , string $boundary ) : string
+ Attach all file, string, and binary attachments to the message.
+ Returns an empty string on failure.
+
+ Parameters
+
+
+ string
+ $disposition_type
+
+
+
+ string
+ $boundary
+
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ encodeFile()
+
+ encodeFile(string $path , string $encoding = 'base64' ) : string
+ Encode a file attachment in requested format.
+ Returns an empty string on failure.
+
+ Parameters
+
+
+ string
+ $path
+ The full path to the file
+
+
+ string
+ $encoding
+ The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+
+
+
+ Throws
+
+ \phpmailerException
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+
+
+
+
+
+ setError()
+
+ setError(string $msg ) : void
+ Add an error message to the error container.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ serverHostname()
+
+ serverHostname() : string
+ Get the server hostname.
+ Returns 'localhost.localdomain' if unknown.
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ lang()
+
+ lang(string $key ) : string
+ Get an error message in the current language.
+
+
+ Parameters
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ doCallback()
+
+ doCallback(boolean $isSent , string $to , string $cc , string $bcc , string $subject , string $body , string $from = null )
+ Perform a callback.
+
+
+ Parameters
+
+
+ boolean
+ $isSent
+
+
+
+ string
+ $to
+
+
+
+ string
+ $cc
+
+
+
+ string
+ $bcc
+
+
+
+ string
+ $subject
+
+
+
+ string
+ $body
+
+
+
+ string
+ $from
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mailPassthru()
+
+ mailPassthru(string $to , string $subject , string $body , string $header , string $params ) : boolean
+ Call mail() in a safe_mode-aware fashion.
+ Also, unless sendmail_path points to sendmail (or something that
+claims to be sendmail), don't pass params (not a perfect fix,
+but it will do)
+
+ Parameters
+
+
+ string
+ $to
+ To
+
+
+ string
+ $subject
+ Subject
+
+
+ string
+ $body
+ Message Body
+
+
+ string
+ $header
+ Additional Header(s)
+
+
+ string
+ $params
+ Params
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/SMTP.html b/doc/classes/SMTP.html
new file mode 100644
index 00000000..1f1811c9
--- /dev/null
+++ b/doc/classes/SMTP.html
@@ -0,0 +1,2653 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \
+
+
+
+
\ SMTP
+
PHPMailer RFC821 SMTP email transport class.
+
Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
+
+
+
+ Summary
+
+
+
+
+
+ No private methods found
+
+
+ No private properties found
+
+
+
+
+
+
+
+
+
+ File
+ app/lib/SMTP.inc
+ Package
+ PHPMailer
+ Class hierarchy
+
+ \SMTP
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+ VERSION
+ VERSION
+ The PHPMailer SMTP version number.
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ CRLF
+ CRLF
+ SMTP line break constant.
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ DEFAULT_SMTP_PORT
+ DEFAULT_SMTP_PORT
+ The SMTP port to use if one is not specified.
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ MAX_LINE_LENGTH
+ MAX_LINE_LENGTH
+ The maximum line length allowed by RFC 2822 section 2.1.1
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $Version
+ $Version :
+ The PHPMailer SMTP Version number.
+
+
+ Type
+
+
+
+
+
+
+ Deprecated
+ Use the constant instead
+
+
+ See also
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $SMTP_PORT
+ $SMTP_PORT :
+ SMTP server port number.
+
+
+ Type
+
+
+
+
+
+
+ Deprecated
+ This is only ever used as a default value, so use the constant instead
+
+
+ See also
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ $CRLF
+ $CRLF :
+ SMTP reply line ending.
+
+
+ Type
+
+
+
+
+
+
+ Deprecated
+ Use the constant instead
+
+
+ See also
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $do_debug
+ $do_debug :
+ Debug output level.
+ Options:
+
+0
No output
+1
Commands
+2
Data and commands
+3
As 2 plus connection status
+4
Low-level data output
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ $Debugoutput
+ $Debugoutput :
+ How to handle debug output.
+ Options:
+
+echo
Output plain-text as-is, appropriate for CLI
+html
Output escaped, line breaks converted to , appropriate for browser output
+error_log
Output to error log as configured in php.ini
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $do_verp
+ $do_verp :
+ Whether to use VERP.
+
+
+ Type
+
+
+
+
+
+
+
+
+
+
+ $Timeout
+ $Timeout :
+ The timeout value for connection, in seconds.
+ Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
+
+ Type
+
+
+
+
+
+
+
+
+
+
+ $Timelimit
+ $Timelimit :
+ The SMTP timelimit value for reads, in seconds.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ int
+
+
+
+
+
+
+
+
+
+
+ $smtp_conn
+ $smtp_conn :
+ The socket for the server connection.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ resource
+
+
+
+
+
+
+
+
+
+
+ $error
+ $error :
+ Error message, if any, for the last call.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $helo_rply
+ $helo_rply :
+ The reply the server sent to us for HELO.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+ $last_reply
+ $last_reply :
+ The most recent reply received from the server.
+
+
+ Type
+
+
+
+
+
+
+
+ Tags
+
+
+
+ type
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct()
+ Constructor.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ connect()
+
+ connect(string $host , integer $port = null , integer $timeout = 30 , array $options = array() ) : boolean
+ Connect to an SMTP server.
+
+
+ Parameters
+
+
+ string
+ $host
+ SMTP server IP or host name
+
+
+ integer
+ $port
+ The port number to connect to
+
+
+ integer
+ $timeout
+ How long to wait for the connection to open
+
+
+ array
+ $options
+ An array of options for stream_context_create()
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ startTLS()
+
+ startTLS() : boolean
+ Initiate a TLS (encrypted) session.
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ authenticate()
+
+ authenticate(string $username , string $password , string $authtype = 'LOGIN' , string $realm = '' , string $workstation = '' ) : boolean
+ Perform SMTP authentication.
+ Must be run after hello().
+
+ Parameters
+
+
+ string
+ $username
+ The user name
+
+
+ string
+ $password
+ The password
+
+
+ string
+ $authtype
+ The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
+
+
+ string
+ $realm
+ The auth realm for NTLM
+
+
+ string
+ $workstation
+ The auth workstation for NTLM
+
+
+
+
+ Returns
+ boolean
+ — True if successfully authenticated.
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+
+
+
+
+
+ connected()
+
+ connected() : boolean
+ Check connection state.
+
+
+
+
+ Returns
+ boolean
+ — True if connected.
+
+
+
+
+
+
+
+
+
+
+ close()
+
+ close() : void
+ Close the socket and clean up the state of the class.
+ Don't use this function without first trying to use QUIT.
+
+
+
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+
+
+
+
+
+ data()
+
+ data(string $msg_data ) : boolean
+ Send an SMTP DATA command.
+ Issues a data command and sends the msg_data to the server,
+finializing the mail transaction. $msg_data is the message
+that is to be send with the headers. Each header needs to be
+on a single line followed by a with the message headers
+and the message body being separated by and additional .
+Implements rfc 821: DATA
+
+ Parameters
+
+
+ string
+ $msg_data
+ Message data to send
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ hello()
+
+ hello(string $host = '' ) : boolean
+ Send an SMTP HELO or EHLO command.
+ Used to identify the sending server to the receiving server.
+This makes sure that client and server are in a known state.
+Implements RFC 821: HELO
+and RFC 2821 EHLO.
+
+ Parameters
+
+
+ string
+ $host
+ The host name or IP to connect to
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ mail()
+
+ mail(string $from ) : boolean
+ Send an SMTP MAIL command.
+ Starts a mail transaction from the email address specified in
+$from. Returns true if successful or false otherwise. If True
+the mail transaction is started and then one or more recipient
+commands may be called followed by a data command.
+Implements rfc 821: MAIL FROM:
+
+ Parameters
+
+
+ string
+ $from
+ Source address of this message
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ quit()
+
+ quit(boolean $close_on_error = true ) : boolean
+ Send an SMTP QUIT command.
+ Closes the socket if there is no error or the $close_on_error argument is true.
+Implements from rfc 821: QUIT
+
+ Parameters
+
+
+ boolean
+ $close_on_error
+ Should the connection close if an error occurs?
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ recipient()
+
+ recipient(string $toaddr ) : boolean
+ Send an SMTP RCPT command.
+ Sets the TO argument to $toaddr.
+Returns true if the recipient was accepted false if it was rejected.
+Implements from rfc 821: RCPT TO:
+
+ Parameters
+
+
+ string
+ $toaddr
+ The address the message is being sent to
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ reset()
+
+ reset() : boolean
+ Send an SMTP RSET command.
+ Abort any transaction that is currently in progress.
+Implements rfc 821: RSET
+
+
+
+ Returns
+ boolean
+ — True on success.
+
+
+
+
+
+
+
+
+
+
+ sendAndMail()
+
+ sendAndMail(string $from ) : boolean
+ Send an SMTP SAML command.
+ Starts a mail transaction from the email address specified in $from.
+Returns true if successful or false otherwise. If True
+the mail transaction is started and then one or more recipient
+commands may be called followed by a data command. This command
+will send the message to the users terminal if they are logged
+in and send them an email.
+Implements rfc 821: SAML FROM:
+
+ Parameters
+
+
+ string
+ $from
+ The address the message is from
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ verify()
+
+ verify(string $name ) : boolean
+ Send an SMTP VRFY command.
+
+
+ Parameters
+
+
+ string
+ $name
+ The name to verify
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ noop()
+
+ noop() : boolean
+ Send an SMTP NOOP command.
+ Used to keep keep-alives alive, doesn't actually do anything
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ turn()
+
+ turn() : boolean
+ Send an SMTP TURN command.
+ This is an optional command for SMTP that this class does not support.
+This method is here to make the RFC821 Definition complete for this class
+and may be implemented in future
+Implements from rfc 821: TURN
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ client_send()
+
+ client_send(string $data ) : integer|boolean
+ Send raw data to the server.
+
+
+ Parameters
+
+
+ string
+ $data
+ The data to send
+
+
+
+
+ Returns
+ integer|boolean
+ — The number of bytes sent to the server or false on error
+
+
+
+
+
+
+
+
+
+
+ getError()
+
+ getError() : array
+ Get the latest error.
+
+
+
+
+ Returns
+ array
+
+
+
+
+
+
+
+
+
+
+ getLastReply()
+
+ getLastReply() : string
+ Get the last reply from the server.
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ setVerp()
+
+ setVerp(boolean $enabled = false )
+ Enable or disable VERP address generation.
+
+
+ Parameters
+
+
+ boolean
+ $enabled
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getVerp()
+
+ getVerp() : boolean
+ Get VERP address generation mode.
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+
+
+
+
+ setDebugOutput()
+
+ setDebugOutput(string $method = 'echo' )
+ Set debug output method.
+
+
+ Parameters
+
+
+ string
+ $method
+ The function/method to use for debugging output.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getDebugOutput()
+
+ getDebugOutput() : string
+ Get debug output method.
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ setDebugLevel()
+
+ setDebugLevel(integer $level )
+ Set debug output level.
+
+
+ Parameters
+
+
+ integer
+ $level
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getDebugLevel()
+
+ getDebugLevel() : integer
+ Get debug output level.
+
+
+
+
+ Returns
+ integer
+
+
+
+
+
+
+
+
+
+
+ setTimeout()
+
+ setTimeout(integer $timeout )
+ Set SMTP timeout.
+
+
+ Parameters
+
+
+ integer
+ $timeout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getTimeout()
+
+ getTimeout() : integer
+ Get SMTP timeout.
+
+
+
+
+ Returns
+ integer
+
+
+
+
+
+
+
+
+
+
+ edebug()
+
+ edebug(string $str ) : void
+ Output debugging info via a user-selected method.
+
+
+ Parameters
+
+
+ string
+ $str
+ Debug string to output
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hmac()
+
+ hmac(string $data , string $key ) : string
+ Calculate an MD5 HMAC hash.
+ Works like hash_hmac('md5', $data, $key)
+in case that function is not available
+
+ Parameters
+
+
+ string
+ $data
+ The data to hash
+
+
+ string
+ $key
+ The key to hash with
+
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+ sendHello()
+
+ sendHello(string $hello , string $host ) : boolean
+ Send an SMTP HELO or EHLO command.
+ Low-level implementation used by hello()
+
+ Parameters
+
+
+ string
+ $hello
+ The HELO string
+
+
+ string
+ $host
+ The hostname to say we are
+
+
+
+
+ Returns
+ boolean
+
+
+
+
+
+
+ See also
+
+
+ Tags
+
+
+
+
+
+
+
+
+ sendCommand()
+
+ sendCommand(string $command , string $commandstring , integer|array $expect ) : boolean
+ Send a command to an SMTP server and check its return code.
+
+
+ Parameters
+
+
+ string
+ $command
+ The command name - not sent to the server
+
+
+ string
+ $commandstring
+ The actual command to send
+
+
+ integer|array
+ $expect
+ One or more expected integer success codes
+
+
+
+
+ Returns
+ boolean
+ — True on success.
+
+
+
+
+
+
+
+
+
+
+ get_lines()
+
+ get_lines() : string
+ Read the SMTP server's response.
+ Either before eof or socket timeout occurs on the operation.
+With SMTP we can tell if we have more lines to read if the
+4th character is '-' symbol. If it is a space then we don't
+need to read anything else.
+
+
+
+ Returns
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.Controller.html b/doc/classes/hhu.z.Controller.html
new file mode 100644
index 00000000..10c09301
--- /dev/null
+++ b/doc/classes/hhu.z.Controller.html
@@ -0,0 +1,1870 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z
+
+
+
+
\hhu\z Controller
+
Abstract class for implementing an application Controller.
+
+
+
+
+ Summary
+
+
+
+
+
+
+ No private properties found
+
+
+
+
+
+
+ abstract
+
+
+
+ File
+ app/Controller.inc
+ Package
+ Default
+ Class hierarchy
+
+
+
+ \hhu\z\Controller
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+
+ $components
+ $components : array
+ Required components
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $agent
+ $agent : \nre\core\Agent
+ Corresponding Agent
+
+
+ Type
+ \nre\core\Agent
+
+
+
+
+
+
+
+
+
+ $view
+ $view : \nre\core\View
+ View of the Controller
+
+
+ Type
+ \nre\core\View
+
+
+
+
+
+
+
+
+
+ $viewData
+ $viewData : array
+ Data to pass to the View
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Logger instance
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $linker
+ $linker : \nre\core\Linker
+ Linker instance
+
+
+ Type
+ \nre\core\Linker
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $controllerName )
+ Load the class of a Controller.
+
+
+ Parameters
+
+
+ string
+ $controllerName
+ Name of the Controller to load
+
+
+
+ Throws
+
+ \nre\exceptions\ControllerNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $controllerName , string $layoutName , string $action , string $agent )
+ Instantiate a Controller (Factory Pattern).
+
+
+ Parameters
+
+
+ string
+ $controllerName
+ Name of the Controller to instantiate
+
+
+ string
+ $layoutName
+ Name of the current Layout
+
+
+ string
+ $action
+ Current Action
+
+
+ string
+ $agent
+ Corresponding Agent
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(string $layoutName , string $action , \nre\core\Agent $agent )
+ Construct a new application Controller.
+
+
+ Parameters
+
+
+ string
+ $layoutName
+ Name of the current Layout
+
+
+ string
+ $action
+ Current Action
+
+
+ \nre\core\Agent
+ $agent
+ Corresponding Agent
+
+
+
+ Throws
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ preFilter()
+
+ preFilter(\nre\core\Request $request , \nre\core\Response $response )
+ Prefilter that is executed before running the Controller.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ postFilter()
+
+ postFilter(\nre\core\Request $request , \nre\core\Response $response )
+ Postfilter that is executed after running the Controller.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response )
+ Run the Controller.
+ This method executes the Action of the Controller defined by
+the current Request.
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $viewData = null ) : string
+ Generate the output.
+
+
+ Parameters
+
+
+ array
+ $viewData
+ Data to pass to the View
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ set()
+
+ set(string $name , mixed $data )
+ Set data for the View.
+
+
+ Parameters
+
+
+ string
+ $name
+ Key
+
+
+ mixed
+ $data
+ Value
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ redirect()
+
+ redirect(string $url )
+ Redirect to the given URL.
+
+
+ Parameters
+
+
+ string
+ $url
+ Relative URL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ checkModels()
+
+ checkModels(string $modelName ) : boolean
+ Check if Models of this Controller are loaded and available.
+
+
+ Parameters
+
+
+ string
+ $modelName
+ Arbitrary number of Models to check
+
+
+
+
+ Returns
+ boolean
+ — All given Models are loaded and available
+
+
+
+
+
+
+
+
+
+
+ getView()
+
+ getView() : \nre\core\View
+ Get the View of the Controller
+
+
+
+
+ Returns
+ \nre\core\View
+ — View of the Controller
+
+
+
+
+
+
+
+
+
+
+ loadModels()
+
+ loadModels()
+ Load the Models of this Controller.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadView()
+
+ loadView(string $layoutName , string $action )
+ Load the View of this Controller.
+
+
+ Parameters
+
+
+ string
+ $layoutName
+ Name of the current Layout
+
+
+ string
+ $action
+ Current Action
+
+
+
+ Throws
+
+ \nre\exceptions\ViewNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(string $message , integer $logMode = \nre\core\Logger::LOGMODE_AUTO )
+ Log an error.
+
+
+ Parameters
+
+
+ string
+ $message
+ Error message to log
+
+
+ integer
+ $logMode
+ Log mode (optional)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $controllerName ) : string
+ Determine the classname for the given Controller name.
+
+
+ Parameters
+
+
+ string
+ $controllerName
+ Controller name to get classname of
+
+
+
+
+ Returns
+ string
+ — Classname for the Controller name
+
+
+
+
+
+
+
+
+
+
+ loadComponents()
+
+ loadComponents()
+ Load the Components of this Controller.
+
+
+
+ Throws
+
+ \nre\exceptions\ComponentNotValidException
+
+ \nre\exceptions\ComponentNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.Model.html b/doc/classes/hhu.z.Model.html
new file mode 100644
index 00000000..666c68b6
--- /dev/null
+++ b/doc/classes/hhu.z.Model.html
@@ -0,0 +1,1292 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z
+
+
+
+
\hhu\z Model
+
Abstract class for implementing an application Model.
+
+
+
+
+ Summary
+
+
+
+
+ No public properties found
+
+
+
+
+
+ No protected methods found
+
+
+
+
+
+
+
+ No private properties found
+
+
+
+
+
+
+
+
+
+ File
+ app/Model.inc
+ Package
+ Default
+ Class hierarchy
+
+
+
+
+
+ \hhu\z\Model
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+
+ $db
+ $db : \nre\models\DatabaseDriver
+ Database connection
+
+
+ Type
+ \nre\models\DatabaseDriver
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct()
+ Construct a new application Model.
+ TODO Catch exception
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $modelName )
+ Load the class of a Model.
+
+
+ Parameters
+
+
+ string
+ $modelName
+ Name of the Model to load
+
+
+
+ Throws
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $modelName )
+ Instantiate a Model (Factory Pattern).
+
+
+ Parameters
+
+
+ string
+ $modelName
+ Name of the Model to instantiate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadDriver()
+
+ loadDriver(string $driverName )
+ Load the database driver.
+
+
+ Parameters
+
+
+ string
+ $driverName
+ Name of the database driver
+
+
+
+ Throws
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\DriverNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ connect()
+
+ connect(string $driverName , array $config )
+ Establish a connection to the database.
+
+
+ Parameters
+
+
+ string
+ $driverName
+ Name of the database driver
+
+
+ array
+ $config
+ Connection settings
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $modelName ) : string
+ Determine the classname for the given Model name.
+
+
+ Parameters
+
+
+ string
+ $modelName
+ Model name to get classname of
+
+
+
+
+ Returns
+ string
+ — Classname fore the Model name
+
+
+
+
+
+
+
+
+
+
+ loadModels()
+
+ loadModels()
+ Load the Models of this Model.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.TextFormatter.html b/doc/classes/hhu.z.TextFormatter.html
new file mode 100644
index 00000000..e5d900e1
--- /dev/null
+++ b/doc/classes/hhu.z.TextFormatter.html
@@ -0,0 +1,1117 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z
+
+
+
+
\hhu\z TextFormatter
+
Class to format text with different syntax tags.
+
+
+
+
+ Summary
+
+
+
+
+ No public properties found
+
+
+
+
+
+ No protected methods found
+
+
+ No protected properties found
+
+
+
+
+
+
+
+
+
+
+ File
+ app/TextFormatter.inc
+ Package
+ Default
+ Class hierarchy
+
+ \hhu\z\TextFormatter
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+
+ $linker
+ $linker : \nre\core\Linker
+ Linker to create links.
+
+
+ Type
+ \nre\core\Linker
+
+
+
+
+
+
+
+
+
+ $Media
+ $Media : \nre\core\Model
+ Media-Model to retrieve media data
+
+
+ Type
+ \nre\core\Model
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Linker $linker )
+ Create a new text formatter.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ t()
+
+ t(string $string ) : string
+ Format a string.
+
+
+ Parameters
+
+
+ string
+ $string
+ String to format
+
+
+
+
+ Returns
+ string
+ — Formatted string
+
+
+
+
+
+
+
+
+
+
+ loadMediaModel()
+
+ loadMediaModel() : boolean
+ Load the Media-Model if it is not loaded
+
+
+
+
+ Returns
+ boolean
+ — Whether the Media-Model has been loaded or not
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.Utils.html b/doc/classes/hhu.z.Utils.html
new file mode 100644
index 00000000..38a7b5a4
--- /dev/null
+++ b/doc/classes/hhu.z.Utils.html
@@ -0,0 +1,1204 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z
+
+
+
+
\hhu\z Utils
+
Class for implementing utility methods.
+
+
+
+
+ Summary
+
+
+
+
+ No public properties found
+
+
+
+
+
+ No protected methods found
+
+
+ No protected properties found
+
+
+
+
+
+ No private methods found
+
+
+ No private properties found
+
+
+
+
+
+
+
+
+
+ File
+ app/Utils.inc
+ Package
+ Default
+ Class hierarchy
+
+ \hhu\z\Utils
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+
+
+ t()
+
+ t(string $string ) : string
+ Mask HTML-chars for save output.
+
+
+ Parameters
+
+
+ string
+ $string
+ String to be masked
+
+
+
+
+ Returns
+ string
+ — Masked string
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ htmlspecialchars_utf8()
+
+ htmlspecialchars_utf8(string $string ) : string
+ ‚htmlspecialchars‘ with support for UTF-8.
+
+
+ Parameters
+
+
+ string
+ $string
+ String to be masked
+
+
+
+
+ Returns
+ string
+ — Masked string
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ shortenString()
+
+ shortenString(string $string , integer $length , integer $scope ) : string
+ Cut a string to the given length but only word boundaries.
+
+
+ Parameters
+
+
+ string
+ $string
+ String to cut
+
+
+ integer
+ $length
+ Length to cut string
+
+
+ integer
+ $scope
+ Maximum length to cut string regardless word boundaries
+
+
+
+
+ Returns
+ string
+ — Cutted string
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ sendMail()
+
+ sendMail(mixed $to , string $messageAction , boolean $html = false , array $params = null , \nre\core\Linker $linker = null )
+ Send an e‑mail.
+
+
+ Parameters
+
+
+ mixed
+ $to
+ One (string) or many (array) receivers
+
+
+ string
+ $messageAction
+ Message Action
+
+
+ boolean
+ $html
+ Whether mail should be formatted as HTML or not
+
+
+ array
+ $params
+ Parameters to pass
+
+
+ \nre\core\Linker
+ $linker
+ Linker instance
+
+
+
+ Throws
+
+ \hhu\z\exceptions\MailingException
+
+
+
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ getMimetype()
+
+ getMimetype(string $filename , string $defaultMimetype = null ) : string
+ Detect Mimetype of a file.
+
+
+ Parameters
+
+
+ string
+ $filename
+ Name of file to detect Mimetype of
+
+
+ string
+ $defaultMimetype
+ Default Mimetype to use
+
+
+
+
+ Returns
+ string
+ — Detected Mimetype of file
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.QuesttypeAgent.html b/doc/classes/hhu.z.agents.QuesttypeAgent.html
new file mode 100644
index 00000000..14dcbe21
--- /dev/null
+++ b/doc/classes/hhu.z.agents.QuesttypeAgent.html
@@ -0,0 +1,2142 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents
+
+
+
+
\hhu\z\agents QuesttypeAgent
+
Abstract class for implementing a QuesttypeAgent.
+
+
+
+
+
+
+ abstract
+
+
+
+ File
+ app/agents/QuesttypeAgent.inc
+ Package
+ Default
+ Class hierarchy
+
+
+
+
+
+ \hhu\z\agents\QuesttypeAgent
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $questtypeName )
+ Load a QuesttypeAgent.
+
+
+ Parameters
+
+
+ string
+ $questtypeName
+ Name of the QuesttypeAgent to load
+
+
+
+ Throws
+
+ \hhu\z\exceptions\QuesttypeAgentNotFoundException
+
+ \hhu\z\exceptions\QuesttypeAgentNotValidException
+
+
+
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $questtypeName , \hhu\z\agents\Request $request , \hhu\z\agents\Response $response , \hhu\z\agents\Logger $log = null )
+ Instantiate a QuesttypeAgent (Factory Pattern).
+
+
+ Parameters
+
+
+ string
+ $questtypeName
+ Name of the QuesttypeAgent to instantiate
+
+
+ \hhu\z\agents\Request
+ $request
+ Current request
+
+
+ \hhu\z\agents\Response
+ $response
+ Current respone
+
+
+ \hhu\z\agents\Logger
+ $log
+ Log-system
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \hhu\z\exceptions\QuesttypeModelNotValidException
+
+ \hhu\z\exceptions\QuesttypeModelNotFoundException
+
+ \hhu\z\exceptions\QuesttypeControllerNotValidException
+
+ \hhu\z\exceptions\QuesttypeControllerNotFoundException
+
+
+
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ checkClass()
+
+ checkClass(string $className , string $parentClassName )
+ Check inheritance of the QuesttypeAgent-class.
+
+
+ Parameters
+
+
+ string
+ $className
+ Name of the class to check
+
+
+ string
+ $parentClassName
+ Name of the parent class
+
+
+
+ Throws
+
+ \nre\exceptions\ClassNotValidException
+
+
+
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ saveAnswersOfCharacter()
+
+ saveAnswersOfCharacter(array $seminary , array $questgroup , array $quest , array $character , array $answers )
+ Save the answers of a Character for a Quest.
+
+
+ Parameters
+
+
+ array
+ $seminary
+ Current Seminary data
+
+
+ array
+ $questgroup
+ Current Questgroup data
+
+
+ array
+ $quest
+ Current Quest data
+
+
+ array
+ $character
+ Current Character data
+
+
+ array
+ $answers
+ Character answers for the Quest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ matchAnswersOfCharacter()
+
+ matchAnswersOfCharacter(array $seminary , array $questgroup , array $quest , array $character , array $answers )
+ Check if answers of a Character for a Quest match the correct ones.
+
+
+ Parameters
+
+
+ array
+ $seminary
+ Current Seminary data
+
+
+ array
+ $questgroup
+ Current Questgroup data
+
+
+ array
+ $quest
+ Current Quest data
+
+
+ array
+ $character
+ Current Character data
+
+
+ array
+ $answers
+ Character answers for the Quest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new QuesttypeAgent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \hhu\z\exceptions\QuesttypeModelNotValidException
+
+ \hhu\z\exceptions\QuesttypeModelNotFoundException
+
+ \hhu\z\exceptions\QuesttypeControllerNotValidException
+
+ \hhu\z\exceptions\QuesttypeControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \hhu\z\exceptions\QuesttypeModelNotValidException
+
+ \hhu\z\exceptions\QuesttypeModelNotFoundException
+
+ \hhu\z\exceptions\QuesttypeControllerNotValidException
+
+ \hhu\z\exceptions\QuesttypeControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $questtypeName , string $agentType = null ) : string
+ Determine the Agent-classname for the given Questtype-name.
+
+
+ Parameters
+
+
+ string
+ $questtypeName
+ Questtype-name to get Agent-classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Questtype-name
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadClass()
+
+ loadClass(string $questtypeName , string $fullClassName )
+ Load the class of a QuesttypeAgent.
+
+
+ Parameters
+
+
+ string
+ $questtypeName
+ Name of the QuesttypeAgent to load
+
+
+ string
+ $fullClassName
+ Name of the class to load
+
+
+
+ Throws
+
+ \nre\exceptions\ClassNotFoundException
+
+
+
+
+
+
+
+
+ static
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.ToplevelAgent.html b/doc/classes/hhu.z.agents.ToplevelAgent.html
new file mode 100644
index 00000000..2ad9720a
--- /dev/null
+++ b/doc/classes/hhu.z.agents.ToplevelAgent.html
@@ -0,0 +1,2264 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents
+
+
+
+
\hhu\z\agents ToplevelAgent
+
Abstract class for implementing an application Controller.
+
+
+
+
+
+
+ abstract
+
+
+
+ File
+ app/agents/ToplevelAgent.inc
+ Package
+ Default
+ Class hierarchy
+
+
+
+
+
+ \hhu\z\agents\ToplevelAgent
+
+
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+ STAGE_LOAD
+ STAGE_LOAD
+ Stage: Load
+
+
+
+
+
+
+
+
+
+
+ STAGE_RUN
+ STAGE_RUN
+ Stage: Run
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+ $layout
+ $layout : \nre\core\Layout
+ Layout instace
+
+
+ Type
+ \nre\core\Layout
+
+
+
+
+
+
+
+
+
+ $intermediateAgent
+ $intermediateAgent : \nre\agents\IntermediateAgent
+ IntermediateAgent instance
+
+
+ Type
+ \nre\agents\IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ getIntermediateAgent()
+
+ getIntermediateAgent() : \nre\agents\IntermediateAgent
+ Return the IntermediateAgent.
+
+
+
+
+ Returns
+ \nre\agents\IntermediateAgent
+ — IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new ToplevlAgent
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadIntermediateAgent()
+
+ loadIntermediateAgent()
+ Load IntermediateAgent defined by the current request.
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _loadIntermediateAgent()
+
+ _loadIntermediateAgent()
+ Load IntermediateAgent defined by the current request.
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _run()
+
+ _run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\AccessDeniedException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ runIntermediateAgent()
+
+ runIntermediateAgent()
+ Run IntermediateAgent.
+
+
+
+ Throws
+
+ \nre\exceptions\AccessDeniedException
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ error()
+
+ error(\Exception $exception , integer $httpStatusCode , string $stage = self::STAGE_LOAD )
+ Handle an error that occurred during
+loading/cnostructing/running of the IntermediateAgent.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $httpStatusCode
+ HTTP-statuscode
+
+
+ string
+ $stage
+ Stage of execution
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.bottomlevel.MailreceiverAgent.html b/doc/classes/hhu.z.agents.bottomlevel.MailreceiverAgent.html
new file mode 100644
index 00000000..70ad0804
--- /dev/null
+++ b/doc/classes/hhu.z.agents.bottomlevel.MailreceiverAgent.html
@@ -0,0 +1,1971 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\bottomlevel
+
+
+
+
\hhu\z\agents\bottomlevel MailreceiverAgent
+
Agent to generate a mail receiver salutation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.bottomlevel.MenuAgent.html b/doc/classes/hhu.z.agents.bottomlevel.MenuAgent.html
new file mode 100644
index 00000000..7bedee57
--- /dev/null
+++ b/doc/classes/hhu.z.agents.bottomlevel.MenuAgent.html
@@ -0,0 +1,1971 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.bottomlevel.QuestgroupshierarchypathAgent.html b/doc/classes/hhu.z.agents.bottomlevel.QuestgroupshierarchypathAgent.html
new file mode 100644
index 00000000..0f8575a4
--- /dev/null
+++ b/doc/classes/hhu.z.agents.bottomlevel.QuestgroupshierarchypathAgent.html
@@ -0,0 +1,1971 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\bottomlevel
+
+
+
+
\hhu\z\agents\bottomlevel QuestgroupshierarchypathAgent
+
Agent to display the Questgroups hierarchy path.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.bottomlevel.SeminarybarAgent.html b/doc/classes/hhu.z.agents.bottomlevel.SeminarybarAgent.html
new file mode 100644
index 00000000..46cc35f9
--- /dev/null
+++ b/doc/classes/hhu.z.agents.bottomlevel.SeminarybarAgent.html
@@ -0,0 +1,1971 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\bottomlevel
+
+
+
+
\hhu\z\agents\bottomlevel SeminarybarAgent
+
Agent to display a sidebar with Seminary related information.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.bottomlevel.SeminarymenuAgent.html b/doc/classes/hhu.z.agents.bottomlevel.SeminarymenuAgent.html
new file mode 100644
index 00000000..4bd07065
--- /dev/null
+++ b/doc/classes/hhu.z.agents.bottomlevel.SeminarymenuAgent.html
@@ -0,0 +1,1971 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\bottomlevel
+
+
+
+
\hhu\z\agents\bottomlevel SeminarymenuAgent
+
Agent to display a menu with Seminary related links.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.bottomlevel.UserrolesAgent.html b/doc/classes/hhu.z.agents.bottomlevel.UserrolesAgent.html
new file mode 100644
index 00000000..74030f3e
--- /dev/null
+++ b/doc/classes/hhu.z.agents.bottomlevel.UserrolesAgent.html
@@ -0,0 +1,1971 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\bottomlevel
+
+
+
+
\hhu\z\agents\bottomlevel UserrolesAgent
+
Agent to display and manage userroles.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ user()
+
+ user(\nre\core\Request $request , \nre\core\Response $response )
+ Action: user.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.AchievementsAgent.html b/doc/classes/hhu.z.agents.intermediate.AchievementsAgent.html
new file mode 100644
index 00000000..e3f78376
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.AchievementsAgent.html
@@ -0,0 +1,2012 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLayout()
+
+ getLayout(string $agentName ) : string
+ Get the layout if it was explicitly defined.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+
+ Returns
+ string
+ — Layout of the IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.CharactergroupsAgent.html b/doc/classes/hhu.z.agents.intermediate.CharactergroupsAgent.html
new file mode 100644
index 00000000..7c43eaec
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.CharactergroupsAgent.html
@@ -0,0 +1,2012 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\intermediate
+
+
+
+
\hhu\z\agents\intermediate CharactergroupsAgent
+
Agent to display Character groups.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLayout()
+
+ getLayout(string $agentName ) : string
+ Get the layout if it was explicitly defined.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+
+ Returns
+ string
+ — Layout of the IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.CharactergroupsquestsAgent.html b/doc/classes/hhu.z.agents.intermediate.CharactergroupsquestsAgent.html
new file mode 100644
index 00000000..d42a9971
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.CharactergroupsquestsAgent.html
@@ -0,0 +1,2012 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\intermediate
+
+
+
+
\hhu\z\agents\intermediate CharactergroupsquestsAgent
+
Agent to display Character groups Quests.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLayout()
+
+ getLayout(string $agentName ) : string
+ Get the layout if it was explicitly defined.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+
+ Returns
+ string
+ — Layout of the IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.CharactersAgent.html b/doc/classes/hhu.z.agents.intermediate.CharactersAgent.html
new file mode 100644
index 00000000..2d3e3f9a
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.CharactersAgent.html
@@ -0,0 +1,2052 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\intermediate
+
+
+
+
\hhu\z\agents\intermediate CharactersAgent
+
Agent to list registered Characters and their data.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLayout()
+
+ getLayout(string $agentName ) : string
+ Get the layout if it was explicitly defined.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+
+ Returns
+ string
+ — Layout of the IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ character()
+
+ character(\nre\core\Request $request , \nre\core\Response $response )
+ Action: character.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.CharactertypesAgent.html b/doc/classes/hhu.z.agents.intermediate.CharactertypesAgent.html
new file mode 100644
index 00000000..c3b552ff
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.CharactertypesAgent.html
@@ -0,0 +1,2012 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\intermediate
+
+
+
+
\hhu\z\agents\intermediate CharactertypesAgent
+
Agent to handle Charactertyes of a Seminary.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLayout()
+
+ getLayout(string $agentName ) : string
+ Get the layout if it was explicitly defined.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+
+ Returns
+ string
+ — Layout of the IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.ErrorAgent.html b/doc/classes/hhu.z.agents.intermediate.ErrorAgent.html
new file mode 100644
index 00000000..b63ed165
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.ErrorAgent.html
@@ -0,0 +1,2012 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLayout()
+
+ getLayout(string $agentName ) : string
+ Get the layout if it was explicitly defined.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+
+ Returns
+ string
+ — Layout of the IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.IntroductionAgent.html b/doc/classes/hhu.z.agents.intermediate.IntroductionAgent.html
new file mode 100644
index 00000000..5fa8154d
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.IntroductionAgent.html
@@ -0,0 +1,2012 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \hhu\z\agents\intermediate
+
+
+
+
\hhu\z\agents\intermediate IntroductionAgent
+
Agent to show an introduction page.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INLINEERROR_AGENT
+ INLINEERROR_AGENT
+ Name of BottomlevelAgent for showing inline errors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $controller
+ $controller : \nre\core\Controller
+ Controller of this Agent
+
+
+ Type
+ \nre\core\Controller
+
+
+
+
+
+
+
+
+
+ $log
+ $log : \nre\core\Logger
+ Log-system
+
+
+ Type
+ \nre\core\Logger
+
+
+
+
+
+
+
+
+
+ $subAgents
+ $subAgents : array
+ SubAgents
+
+
+ Type
+ array
+
+
+
+
+
+
+
+
+
+ $request
+ $request : \nre\core\Request
+ Current request
+
+
+ Type
+ \nre\core\Request
+
+
+
+
+
+
+
+
+
+ $response
+ $response : \nre\core\Response
+ Current response
+
+
+ Type
+ \nre\core\Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLayout()
+
+ getLayout(string $agentName ) : string
+ Get the layout if it was explicitly defined.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+
+ Returns
+ string
+ — Layout of the IntermediateAgent
+
+
+
+
+
+
+
+
+
+
+ load()
+
+ load(string $agentName )
+ Load the class of an Agent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ factory()
+
+ factory(string $agentName , \nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Instantiate an Agent (Factory Pattern).
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run()
+
+ run(\nre\core\Request $request , \nre\core\Response $response ) : \Exception
+ Run the Controller of this Agent and its SubAgents.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\ParamsNotValidException
+
+ \nre\exceptions\IdNotFoundException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\ActionNotFoundException
+
+
+
+ Returns
+ \Exception
+ — Last occurred exception of SubAgents
+
+
+
+
+
+
+
+
+
+
+ render()
+
+ render(array $data = array() ) : string
+ Generate output of the Controller of this Agent and its
+SubAgents.
+
+
+ Parameters
+
+
+ array
+ $data
+ View data
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ index()
+
+ index(\nre\core\Request $request , \nre\core\Response $response )
+ Action: index.
+
+
+ Parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __construct()
+
+ __construct(\nre\core\Request $request , \nre\core\Response $response , \nre\core\Logger $log = null )
+ Construct a new Agent.
+
+
+ Parameters
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ actionConstruct()
+
+ actionConstruct()
+ Construct SubAgents (per Action).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loadController()
+
+ loadController()
+ Load the Controller of this Agent.
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\DriverNotValidException
+
+ \nre\exceptions\DriverNotFoundException
+
+ \nre\exceptions\ViewNotFoundException
+
+ \nre\exceptions\ModelNotValidException
+
+ \nre\exceptions\ModelNotFoundException
+
+ \nre\exceptions\ControllerNotValidException
+
+ \nre\exceptions\ControllerNotFoundException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log()
+
+ log(\Exception $exception , integer $logMode )
+ Log an error.
+
+
+ Parameters
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+ integer
+ $logMode
+ Log mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addSubAgent()
+
+ addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _addSubAgent()
+
+ _addSubAgent(string $agentName )
+ Load a SubAgent and add it.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Name of the Agent to load
+
+
+
+ Throws
+
+ \nre\exceptions\ServiceUnavailableException
+
+ \nre\exceptions\DatamodelException
+
+ \nre\exceptions\AgentNotFoundException
+
+ \nre\exceptions\AgentNotValidException
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAgentType()
+
+ getAgentType() : string
+ Determine the type of an Agent.
+
+
+
+
+ Returns
+ string
+ — Agent type
+
+
+
+
+
+
+
+
+
+
+ getClassName()
+
+ getClassName(string $agentName , string $agentType ) : string
+ Determine the classname for the given Agent name.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name to get classname of
+
+
+ string
+ $agentType
+ Agent type of given Agent name
+
+
+
+
+ Returns
+ string
+ — Classname for the Agent name
+
+
+
+
+
+
+
+
+
+
+ newSubAgent()
+
+ newSubAgent(string $agentName ) : array
+ Create a new SubAgent.
+
+
+ Parameters
+
+
+ string
+ $agentName
+ Agent name
+
+
+
+ Throws
+
+ \nre\exceptions\DatamodelException
+
+
+
+ Returns
+ array
+ — SubAgent
+
+
+
+
+
+
+
+
+
+
+ renderSubAgent()
+
+ renderSubAgent(array $subAgent ) : string
+ Render a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ SubAgent to render
+
+
+
+
+ Returns
+ string
+ — Generated output
+
+
+
+
+
+
+
+
+
+
+ errorInline()
+
+ errorInline(array $subAgent , \nre\core\Request $request , \nre\core\Excepiton $exception ) : array
+ Handle the exception of a SubAgent.
+
+
+ Parameters
+
+
+ array
+ $subAgent
+ Original (Sub-) Agent
+
+
+ \nre\core\Request
+ $request
+ Current request
+
+
+ \nre\core\Excepiton
+ $exception
+ Occurred exception
+
+
+
+
+ Returns
+ array
+ — InlineError-SubAgent
+
+
+
+
+
+
+
+
+
+
+ newInlineError()
+
+ newInlineError(string $label , \Exception $exception )
+ Create a new InlineError.
+
+
+ Parameters
+
+
+ string
+ $label
+ Name of the original Agent
+
+
+ \Exception
+ $exception
+ Occurred exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ newInlineErrorService()
+
+ newInlineErrorService()
+ Handle a hardcore error that could not be handled by the
+system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Documentation is powered by phpDocumentor and authored
+ on March 6th, 2015 at 11:17.
+
+
+
+
+
+
+
+
diff --git a/doc/classes/hhu.z.agents.intermediate.LibraryAgent.html b/doc/classes/hhu.z.agents.intermediate.LibraryAgent.html
new file mode 100644
index 00000000..5197a5b2
--- /dev/null
+++ b/doc/classes/hhu.z.agents.intermediate.LibraryAgent.html
@@ -0,0 +1,2012 @@
+
+
+
+
+
+ Questlab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+