+ $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/Phpqrcode-lib.inc b/app/lib/Phpqrcode-lib.inc
new file mode 100644
index 00000000..3728e625
--- /dev/null
+++ b/app/lib/Phpqrcode-lib.inc
@@ -0,0 +1,33 @@
+
+ * @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 Phpqrcode
+ {
+
+ /**
+ * Call this function to load necessary files.
+ */
+ public static function load()
+ {
+ require_once('phpqrcode'.DS.'qrlib.php');
+ }
+
+ }
+
+?>
diff --git a/app/lib/Phpqrcode.inc b/app/lib/Phpqrcode.inc
new file mode 100644
index 00000000..f47b72ac
--- /dev/null
+++ b/app/lib/Phpqrcode.inc
@@ -0,0 +1,3346 @@
+
+ * @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 Phpqrcode
+ {
+
+ /**
+ * Call this function to ensure this file is loaded.
+ */
+ public static function load()
+ {
+ }
+
+ }
+
+ }
+
+ namespace {
+
+/*
+ * PHP QR Code encoder
+ *
+ * This file contains MERGED version of PHP QR Code library.
+ * It was auto-generated from full version for your convenience.
+ *
+ * This merged version was configured to not requre any external files,
+ * with disabled cache, error loging and weker but faster mask matching.
+ * If you need tune it up please use non-merged version.
+ *
+ * For full version, documentation, examples of use please visit:
+ *
+ * http://phpqrcode.sourceforge.net/
+ * https://sourceforge.net/projects/phpqrcode/
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+
+/*
+ * Version: 1.1.4
+ * Build: 2010100721
+ */
+
+
+
+//---- qrconst.php -----------------------------
+
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Common constants
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ // Encoding modes
+
+ define('QR_MODE_NUL', -1);
+ define('QR_MODE_NUM', 0);
+ define('QR_MODE_AN', 1);
+ define('QR_MODE_8', 2);
+ define('QR_MODE_KANJI', 3);
+ define('QR_MODE_STRUCTURE', 4);
+
+ // Levels of error correction.
+
+ define('QR_ECLEVEL_L', 0);
+ define('QR_ECLEVEL_M', 1);
+ define('QR_ECLEVEL_Q', 2);
+ define('QR_ECLEVEL_H', 3);
+
+ // Supported output formats
+
+ define('QR_FORMAT_TEXT', 0);
+ define('QR_FORMAT_PNG', 1);
+
+ class qrstr {
+ public static function set(&$srctab, $x, $y, $repl, $replLen = false) {
+ $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
+ }
+ }
+
+
+
+//---- merged_config.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Config file, tuned-up for merged verion
+ */
+
+ define('QR_CACHEABLE', false); // use cache - more disk reads but less CPU power, masks and format templates are stored there
+ define('QR_CACHE_DIR', false); // used when QR_CACHEABLE === true
+ define('QR_LOG_DIR', false); // default error logs dir
+
+ define('QR_FIND_BEST_MASK', true); // if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code
+ define('QR_FIND_FROM_RANDOM', 2); // if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly
+ define('QR_DEFAULT_MASK', 2); // when QR_FIND_BEST_MASK === false
+
+ define('QR_PNG_MAXIMUM_SIZE', 1024); // maximum allowed png image width (in pixels), tune to make sure GD and PHP can handle such big images
+
+
+
+
+//---- qrtools.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Toolset, handy and debug utilites.
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRtools {
+
+ //----------------------------------------------------------------------
+ public static function binarize($frame)
+ {
+ $len = count($frame);
+ foreach ($frame as &$frameLine) {
+
+ for($i=0; $i<$len; $i++) {
+ $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
+ }
+ }
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function tcpdfBarcodeArray($code, $mode = 'QR,L', $tcPdfVersion = '4.5.037')
+ {
+ $barcode_array = array();
+
+ if (!is_array($mode))
+ $mode = explode(',', $mode);
+
+ $eccLevel = 'L';
+
+ if (count($mode) > 1) {
+ $eccLevel = $mode[1];
+ }
+
+ $qrTab = QRcode::text($code, false, $eccLevel);
+ $size = count($qrTab);
+
+ $barcode_array['num_rows'] = $size;
+ $barcode_array['num_cols'] = $size;
+ $barcode_array['bcode'] = array();
+
+ foreach ($qrTab as $line) {
+ $arrAdd = array();
+ foreach(str_split($line) as $char)
+ $arrAdd[] = ($char=='1')?1:0;
+ $barcode_array['bcode'][] = $arrAdd;
+ }
+
+ return $barcode_array;
+ }
+
+ //----------------------------------------------------------------------
+ public static function clearCache()
+ {
+ self::$frames = array();
+ }
+
+ //----------------------------------------------------------------------
+ public static function buildCache()
+ {
+ QRtools::markTime('before_build_cache');
+
+ $mask = new QRmask();
+ for ($a=1; $a <= QRSPEC_VERSION_MAX; $a++) {
+ $frame = QRspec::newFrame($a);
+ if (QR_IMAGE) {
+ $fileName = QR_CACHE_DIR.'frame_'.$a.'.png';
+ QRimage::png(self::binarize($frame), $fileName, 1, 0);
+ }
+
+ $width = count($frame);
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+ for ($maskNo=0; $maskNo<8; $maskNo++)
+ $mask->makeMaskNo($maskNo, $width, $frame, $bitMask, true);
+ }
+
+ QRtools::markTime('after_build_cache');
+ }
+
+ //----------------------------------------------------------------------
+ public static function log($outfile, $err)
+ {
+ if (QR_LOG_DIR !== false) {
+ if ($err != '') {
+ if ($outfile !== false) {
+ file_put_contents(QR_LOG_DIR.basename($outfile).'-errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ } else {
+ file_put_contents(QR_LOG_DIR.'errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function dumpMask($frame)
+ {
+ $width = count($frame);
+ for($y=0;$y<$width;$y++) {
+ for($x=0;$x<$width;$x++) {
+ echo ord($frame[$y][$x]).',';
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function markTime($markerId)
+ {
+ list($usec, $sec) = explode(" ", microtime());
+ $time = ((float)$usec + (float)$sec);
+
+ if (!isset($GLOBALS['qr_time_bench']))
+ $GLOBALS['qr_time_bench'] = array();
+
+ $GLOBALS['qr_time_bench'][$markerId] = $time;
+ }
+
+ //----------------------------------------------------------------------
+ public static function timeBenchmark()
+ {
+ self::markTime('finish');
+
+ $lastTime = 0;
+ $startTime = 0;
+ $p = 0;
+
+ echo '
+ BENCHMARK
+ ';
+
+ foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) {
+ if ($p > 0) {
+ echo 'till '.$markerId.': '.number_format($thisTime-$lastTime, 6).'s ';
+ } else {
+ $startTime = $thisTime;
+ }
+
+ $p++;
+ $lastTime = $thisTime;
+ }
+
+ echo '
+ TOTAL: '.number_format($lastTime-$startTime, 6).'s
+
+
';
+ }
+
+ }
+
+ //##########################################################################
+
+ QRtools::markTime('start');
+
+
+
+
+//---- qrspec.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * QR Code specifications
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('QRSPEC_VERSION_MAX', 40);
+ define('QRSPEC_WIDTH_MAX', 177);
+
+ define('QRCAP_WIDTH', 0);
+ define('QRCAP_WORDS', 1);
+ define('QRCAP_REMINDER', 2);
+ define('QRCAP_EC', 3);
+
+ class QRspec {
+
+ public static $capacity = array(
+ array( 0, 0, 0, array( 0, 0, 0, 0)),
+ array( 21, 26, 0, array( 7, 10, 13, 17)), // 1
+ array( 25, 44, 7, array( 10, 16, 22, 28)),
+ array( 29, 70, 7, array( 15, 26, 36, 44)),
+ array( 33, 100, 7, array( 20, 36, 52, 64)),
+ array( 37, 134, 7, array( 26, 48, 72, 88)), // 5
+ array( 41, 172, 7, array( 36, 64, 96, 112)),
+ array( 45, 196, 0, array( 40, 72, 108, 130)),
+ array( 49, 242, 0, array( 48, 88, 132, 156)),
+ array( 53, 292, 0, array( 60, 110, 160, 192)),
+ array( 57, 346, 0, array( 72, 130, 192, 224)), //10
+ array( 61, 404, 0, array( 80, 150, 224, 264)),
+ array( 65, 466, 0, array( 96, 176, 260, 308)),
+ array( 69, 532, 0, array( 104, 198, 288, 352)),
+ array( 73, 581, 3, array( 120, 216, 320, 384)),
+ array( 77, 655, 3, array( 132, 240, 360, 432)), //15
+ array( 81, 733, 3, array( 144, 280, 408, 480)),
+ array( 85, 815, 3, array( 168, 308, 448, 532)),
+ array( 89, 901, 3, array( 180, 338, 504, 588)),
+ array( 93, 991, 3, array( 196, 364, 546, 650)),
+ array( 97, 1085, 3, array( 224, 416, 600, 700)), //20
+ array(101, 1156, 4, array( 224, 442, 644, 750)),
+ array(105, 1258, 4, array( 252, 476, 690, 816)),
+ array(109, 1364, 4, array( 270, 504, 750, 900)),
+ array(113, 1474, 4, array( 300, 560, 810, 960)),
+ array(117, 1588, 4, array( 312, 588, 870, 1050)), //25
+ array(121, 1706, 4, array( 336, 644, 952, 1110)),
+ array(125, 1828, 4, array( 360, 700, 1020, 1200)),
+ array(129, 1921, 3, array( 390, 728, 1050, 1260)),
+ array(133, 2051, 3, array( 420, 784, 1140, 1350)),
+ array(137, 2185, 3, array( 450, 812, 1200, 1440)), //30
+ array(141, 2323, 3, array( 480, 868, 1290, 1530)),
+ array(145, 2465, 3, array( 510, 924, 1350, 1620)),
+ array(149, 2611, 3, array( 540, 980, 1440, 1710)),
+ array(153, 2761, 3, array( 570, 1036, 1530, 1800)),
+ array(157, 2876, 0, array( 570, 1064, 1590, 1890)), //35
+ array(161, 3034, 0, array( 600, 1120, 1680, 1980)),
+ array(165, 3196, 0, array( 630, 1204, 1770, 2100)),
+ array(169, 3362, 0, array( 660, 1260, 1860, 2220)),
+ array(173, 3532, 0, array( 720, 1316, 1950, 2310)),
+ array(177, 3706, 0, array( 750, 1372, 2040, 2430)) //40
+ );
+
+ //----------------------------------------------------------------------
+ public static function getDataLength($version, $level)
+ {
+ return self::$capacity[$version][QRCAP_WORDS] - self::$capacity[$version][QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getECCLength($version, $level)
+ {
+ return self::$capacity[$version][QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getWidth($version)
+ {
+ return self::$capacity[$version][QRCAP_WIDTH];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getRemainder($version)
+ {
+ return self::$capacity[$version][QRCAP_REMINDER];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getMinimumVersion($size, $level)
+ {
+
+ for($i=1; $i<= QRSPEC_VERSION_MAX; $i++) {
+ $words = self::$capacity[$i][QRCAP_WORDS] - self::$capacity[$i][QRCAP_EC][$level];
+ if($words >= $size)
+ return $i;
+ }
+
+ return -1;
+ }
+
+ //######################################################################
+
+ public static $lengthTableBits = array(
+ array(10, 12, 14),
+ array( 9, 11, 13),
+ array( 8, 16, 16),
+ array( 8, 10, 12)
+ );
+
+ //----------------------------------------------------------------------
+ public static function lengthIndicator($mode, $version)
+ {
+ if ($mode == QR_MODE_STRUCTURE)
+ return 0;
+
+ if ($version <= 9) {
+ $l = 0;
+ } else if ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ return self::$lengthTableBits[$mode][$l];
+ }
+
+ //----------------------------------------------------------------------
+ public static function maximumWords($mode, $version)
+ {
+ if($mode == QR_MODE_STRUCTURE)
+ return 3;
+
+ if($version <= 9) {
+ $l = 0;
+ } else if($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ $bits = self::$lengthTableBits[$mode][$l];
+ $words = (1 << $bits) - 1;
+
+ if($mode == QR_MODE_KANJI) {
+ $words *= 2; // the number of bytes is required
+ }
+
+ return $words;
+ }
+
+ // Error correction code -----------------------------------------------
+ // Table of the error correction code (Reed-Solomon block)
+ // See Table 12-16 (pp.30-36), JIS X0510:2004.
+
+ public static $eccTable = array(
+ array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)),
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)),
+ array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5
+ array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)),
+ array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)),
+ array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)),
+ array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)),
+ array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), //10
+ array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)),
+ array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)),
+ array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)),
+ array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)),
+ array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), //15
+ array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)),
+ array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)),
+ array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)),
+ array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)),
+ array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), //20
+ array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)),
+ array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)),
+ array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)),
+ array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)),
+ array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), //25
+ array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)),
+ array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)),
+ array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)),
+ array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)),
+ array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), //30
+ array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)),
+ array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)),
+ array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)),
+ array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)),
+ array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), //35
+ array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)),
+ array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)),
+ array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)),
+ array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)),
+ array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),//40
+ );
+
+ //----------------------------------------------------------------------
+ // CACHEABLE!!!
+
+ public static function getEccSpec($version, $level, array &$spec)
+ {
+ if (count($spec) < 5) {
+ $spec = array(0,0,0,0,0);
+ }
+
+ $b1 = self::$eccTable[$version][$level][0];
+ $b2 = self::$eccTable[$version][$level][1];
+ $data = self::getDataLength($version, $level);
+ $ecc = self::getECCLength($version, $level);
+
+ if($b2 == 0) {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / $b1);
+ $spec[2] = (int)($ecc / $b1);
+ $spec[3] = 0;
+ $spec[4] = 0;
+ } else {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / ($b1 + $b2));
+ $spec[2] = (int)($ecc / ($b1 + $b2));
+ $spec[3] = $b2;
+ $spec[4] = $spec[1] + 1;
+ }
+ }
+
+ // Alignment pattern ---------------------------------------------------
+
+ // Positions of alignment patterns.
+ // This array includes only the second and the third position of the
+ // alignment patterns. Rest of them can be calculated from the distance
+ // between them.
+
+ // See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
+
+ public static $alignmentPattern = array(
+ array( 0, 0),
+ array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5
+ array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10
+ array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), //11-15
+ array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), //16-20
+ array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), //21-25
+ array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), //26-30
+ array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), //31-35
+ array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40
+ );
+
+
+ /** --------------------------------------------------------------------
+ * Put an alignment marker.
+ * @param frame
+ * @param width
+ * @param ox,oy center coordinate of the pattern
+ */
+ public static function putAlignmentMarker(array &$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xa1\xa1\xa1\xa1\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa0\xa1\xa0\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa1\xa1\xa1\xa1"
+ );
+
+ $yStart = $oy-2;
+ $xStart = $ox-2;
+
+ for($y=0; $y<5; $y++) {
+ QRstr::set($frame, $xStart, $yStart+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function putAlignmentPattern($version, &$frame, $width)
+ {
+ if($version < 2)
+ return;
+
+ $d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0];
+ if($d < 0) {
+ $w = 2;
+ } else {
+ $w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2);
+ }
+
+ if($w * $w - 3 == 1) {
+ $x = self::$alignmentPattern[$version][0];
+ $y = self::$alignmentPattern[$version][0];
+ self::putAlignmentMarker($frame, $x, $y);
+ return;
+ }
+
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=1; $x<$w - 1; $x++) {
+ self::putAlignmentMarker($frame, 6, $cx);
+ self::putAlignmentMarker($frame, $cx, 6);
+ $cx += $d;
+ }
+
+ $cy = self::$alignmentPattern[$version][0];
+ for($y=0; $y<$w-1; $y++) {
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=0; $x<$w-1; $x++) {
+ self::putAlignmentMarker($frame, $cx, $cy);
+ $cx += $d;
+ }
+ $cy += $d;
+ }
+ }
+
+ // Version information pattern -----------------------------------------
+
+ // Version information pattern (BCH coded).
+ // See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
+
+ // size: [QRSPEC_VERSION_MAX - 6]
+
+ public static $versionPattern = array(
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
+ 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
+ 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
+ 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
+ 0x27541, 0x28c69
+ );
+
+ //----------------------------------------------------------------------
+ public static function getVersionPattern($version)
+ {
+ if($version < 7 || $version > QRSPEC_VERSION_MAX)
+ return 0;
+
+ return self::$versionPattern[$version -7];
+ }
+
+ // Format information --------------------------------------------------
+ // See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib)
+
+ public static $formatInfo = array(
+ array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976),
+ array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0),
+ array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed),
+ array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b)
+ );
+
+ public static function getFormatInfo($mask, $level)
+ {
+ if($mask < 0 || $mask > 7)
+ return 0;
+
+ if($level < 0 || $level > 3)
+ return 0;
+
+ return self::$formatInfo[$level][$mask];
+ }
+
+ // Frame ---------------------------------------------------------------
+ // Cache of initial frames.
+
+ public static $frames = array();
+
+ /** --------------------------------------------------------------------
+ * Put a finder pattern.
+ * @param frame
+ * @param width
+ * @param ox,oy upper-left coordinate of the pattern
+ */
+ public static function putFinderPattern(&$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
+ );
+
+ for($y=0; $y<7; $y++) {
+ QRstr::set($frame, $ox, $oy+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function createFrame($version)
+ {
+ $width = self::$capacity[$version][QRCAP_WIDTH];
+ $frameLine = str_repeat ("\0", $width);
+ $frame = array_fill(0, $width, $frameLine);
+
+ // Finder pattern
+ self::putFinderPattern($frame, 0, 0);
+ self::putFinderPattern($frame, $width - 7, 0);
+ self::putFinderPattern($frame, 0, $width - 7);
+
+ // Separator
+ $yOffset = $width - 7;
+
+ for($y=0; $y<7; $y++) {
+ $frame[$y][7] = "\xc0";
+ $frame[$y][$width - 8] = "\xc0";
+ $frame[$yOffset][7] = "\xc0";
+ $yOffset++;
+ }
+
+ $setPattern = str_repeat("\xc0", 8);
+
+ QRstr::set($frame, 0, 7, $setPattern);
+ QRstr::set($frame, $width-8, 7, $setPattern);
+ QRstr::set($frame, 0, $width - 8, $setPattern);
+
+ // Format info
+ $setPattern = str_repeat("\x84", 9);
+ QRstr::set($frame, 0, 8, $setPattern);
+ QRstr::set($frame, $width - 8, 8, $setPattern, 8);
+
+ $yOffset = $width - 8;
+
+ for($y=0; $y<8; $y++,$yOffset++) {
+ $frame[$y][8] = "\x84";
+ $frame[$yOffset][8] = "\x84";
+ }
+
+ // Timing pattern
+
+ for($i=1; $i<$width-15; $i++) {
+ $frame[6][7+$i] = chr(0x90 | ($i & 1));
+ $frame[7+$i][6] = chr(0x90 | ($i & 1));
+ }
+
+ // Alignment pattern
+ self::putAlignmentPattern($version, $frame, $width);
+
+ // Version information
+ if($version >= 7) {
+ $vinf = self::getVersionPattern($version);
+
+ $v = $vinf;
+
+ for($x=0; $x<6; $x++) {
+ for($y=0; $y<3; $y++) {
+ $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+
+ $v = $vinf;
+ for($y=0; $y<6; $y++) {
+ for($x=0; $x<3; $x++) {
+ $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+ }
+
+ // and a little bit...
+ $frame[$width - 8][8] = "\x81";
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function debug($frame, $binary_mode = false)
+ {
+ if ($binary_mode) {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join(' ', explode('0', $frameLine));
+ $frameLine = join('██', explode('1', $frameLine));
+ }
+
+ ?>
+
+ ';
+ echo join(" ", $frame);
+ echo ' ';
+
+ } else {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join(' ', explode("\xc0", $frameLine));
+ $frameLine = join('▒ ', explode("\xc1", $frameLine));
+ $frameLine = join(' ', explode("\xa0", $frameLine));
+ $frameLine = join('▒ ', explode("\xa1", $frameLine));
+ $frameLine = join('◇ ', explode("\x84", $frameLine)); //format 0
+ $frameLine = join('◆ ', explode("\x85", $frameLine)); //format 1
+ $frameLine = join('☢ ', explode("\x81", $frameLine)); //special bit
+ $frameLine = join(' ', explode("\x90", $frameLine)); //clock 0
+ $frameLine = join('◷ ', explode("\x91", $frameLine)); //clock 1
+ $frameLine = join(' ', explode("\x88", $frameLine)); //version
+ $frameLine = join('▒ ', explode("\x89", $frameLine)); //version
+ $frameLine = join('♦', explode("\x01", $frameLine));
+ $frameLine = join('⋅', explode("\0", $frameLine));
+ }
+
+ ?>
+
+ ";
+ echo join(" ", $frame);
+ echo " ";
+
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($frame)
+ {
+ return gzcompress(join("\n", $frame), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ return explode("\n", gzuncompress($code));
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFrame($version)
+ {
+ if($version < 1 || $version > QRSPEC_VERSION_MAX)
+ return null;
+
+ if(!isset(self::$frames[$version])) {
+
+ $fileName = QR_CACHE_DIR.'frame_'.$version.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ self::$frames[$version] = self::unserial(file_get_contents($fileName));
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ file_put_contents($fileName, self::serial(self::$frames[$version]));
+ }
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ }
+ }
+
+ if(is_null(self::$frames[$version]))
+ return null;
+
+ return self::$frames[$version];
+ }
+
+ //----------------------------------------------------------------------
+ public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; }
+ public static function rsBlockNum1($spec) { return $spec[0]; }
+ public static function rsDataCodes1($spec) { return $spec[1]; }
+ public static function rsEccCodes1($spec) { return $spec[2]; }
+ public static function rsBlockNum2($spec) { return $spec[3]; }
+ public static function rsDataCodes2($spec) { return $spec[4]; }
+ public static function rsEccCodes2($spec) { return $spec[2]; }
+ public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); }
+ public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; }
+
+ }
+
+
+
+//---- qrimage.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Image output of code using GD2
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('QR_IMAGE', true);
+
+ class QRimage {
+
+ //----------------------------------------------------------------------
+ public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/png");
+ ImagePng($image);
+ } else {
+ if($saveandprint===TRUE){
+ ImagePng($image, $filename);
+ header("Content-type: image/png");
+ ImagePng($image);
+ }else{
+ ImagePng($image, $filename);
+ }
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/jpeg");
+ ImageJpeg($image, null, $q);
+ } else {
+ ImageJpeg($image, $filename, $q);
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ private static function image($frame, $pixelPerPoint = 4, $outerFrame = 4)
+ {
+ $h = count($frame);
+ $w = strlen($frame[0]);
+
+ $imgW = $w + 2*$outerFrame;
+ $imgH = $h + 2*$outerFrame;
+
+ $base_image =ImageCreate($imgW, $imgH);
+
+ $col[0] = ImageColorAllocate($base_image,255,255,255);
+ $col[1] = ImageColorAllocate($base_image,0,0,0);
+
+ imagefill($base_image, 0, 0, $col[0]);
+
+ for($y=0; $y<$h; $y++) {
+ for($x=0; $x<$w; $x++) {
+ if ($frame[$y][$x] == '1') {
+ ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]);
+ }
+ }
+ }
+
+ $target_image =ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint);
+ ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH);
+ ImageDestroy($base_image);
+
+ return $target_image;
+ }
+ }
+
+
+
+//---- qrinput.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Input encoding class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('STRUCTURE_HEADER_BITS', 20);
+ define('MAX_STRUCTURED_SYMBOLS', 16);
+
+ class QRinputItem {
+
+ public $mode;
+ public $size;
+ public $data;
+ public $bstream;
+
+ public function __construct($mode, $size, $data, $bstream = null)
+ {
+ $setData = array_slice($data, 0, $size);
+
+ if (count($setData) < $size) {
+ $setData = array_merge($setData, array_fill(0,$size-count($setData),0));
+ }
+
+ if(!QRinput::check($mode, $size, $setData)) {
+ throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData));
+ return null;
+ }
+
+ $this->mode = $mode;
+ $this->size = $size;
+ $this->data = $setData;
+ $this->bstream = $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeNum($version)
+ {
+ try {
+
+ $words = (int)($this->size / 3);
+ $bs = new QRbitstream();
+
+ $val = 0x1;
+ $bs->appendNum(4, $val);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (ord($this->data[$i*3 ]) - ord('0')) * 100;
+ $val += (ord($this->data[$i*3+1]) - ord('0')) * 10;
+ $val += (ord($this->data[$i*3+2]) - ord('0'));
+ $bs->appendNum(10, $val);
+ }
+
+ if($this->size - $words * 3 == 1) {
+ $val = ord($this->data[$words*3]) - ord('0');
+ $bs->appendNum(4, $val);
+ } else if($this->size - $words * 3 == 2) {
+ $val = (ord($this->data[$words*3 ]) - ord('0')) * 10;
+ $val += (ord($this->data[$words*3+1]) - ord('0'));
+ $bs->appendNum(7, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeAn($version)
+ {
+ try {
+ $words = (int)($this->size / 2);
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x02);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45;
+ $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1]));
+
+ $bs->appendNum(11, $val);
+ }
+
+ if($this->size & 1) {
+ $val = QRinput::lookAnTable(ord($this->data[$words * 2]));
+ $bs->appendNum(6, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeMode8($version)
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x4);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size);
+
+ for($i=0; $i<$this->size; $i++) {
+ $bs->appendNum(8, ord($this->data[$i]));
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeKanji($version)
+ {
+ try {
+
+ $bs = new QRbitrtream();
+
+ $bs->appendNum(4, 0x8);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2));
+
+ for($i=0; $i<$this->size; $i+=2) {
+ $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);
+ if($val <= 0x9ffc) {
+ $val -= 0x8140;
+ } else {
+ $val -= 0xc140;
+ }
+
+ $h = ($val >> 8) * 0xc0;
+ $val = ($val & 0xff) + $h;
+
+ $bs->appendNum(13, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeStructure()
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x03);
+ $bs->appendNum(4, ord($this->data[1]) - 1);
+ $bs->appendNum(4, ord($this->data[0]) - 1);
+ $bs->appendNum(8, ord($this->data[2]));
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSizeOfEntry($version)
+ {
+ $bits = 0;
+
+ if($version == 0)
+ $version = 1;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break;
+ case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break;
+ case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break;
+ case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break;
+ case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS;
+ default:
+ return 0;
+ }
+
+ $l = QRspec::lengthIndicator($this->mode, $version);
+ $m = 1 << $l;
+ $num = (int)(($this->size + $m - 1) / $m);
+
+ $bits += $num * (4 + $l);
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeBitStream($version)
+ {
+ try {
+
+ unset($this->bstream);
+ $words = QRspec::maximumWords($this->mode, $version);
+
+ if($this->size > $words) {
+
+ $st1 = new QRinputItem($this->mode, $words, $this->data);
+ $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words));
+
+ $st1->encodeBitStream($version);
+ $st2->encodeBitStream($version);
+
+ $this->bstream = new QRbitstream();
+ $this->bstream->append($st1->bstream);
+ $this->bstream->append($st2->bstream);
+
+ unset($st1);
+ unset($st2);
+
+ } else {
+
+ $ret = 0;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break;
+ case QR_MODE_AN: $ret = $this->encodeModeAn($version); break;
+ case QR_MODE_8: $ret = $this->encodeMode8($version); break;
+ case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break;
+ case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break;
+
+ default:
+ break;
+ }
+
+ if($ret < 0)
+ return -1;
+ }
+
+ return $this->bstream->size();
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+ };
+
+ //##########################################################################
+
+ class QRinput {
+
+ public $items;
+
+ private $version;
+ private $level;
+
+ //----------------------------------------------------------------------
+ public function __construct($version = 0, $level = QR_ECLEVEL_L)
+ {
+ if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid version no');
+ return NULL;
+ }
+
+ $this->version = $version;
+ $this->level = $level;
+ }
+
+ //----------------------------------------------------------------------
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ //----------------------------------------------------------------------
+ public function setVersion($version)
+ {
+ if($version < 0 || $version > QRSPEC_VERSION_MAX) {
+ throw new Exception('Invalid version no');
+ return -1;
+ }
+
+ $this->version = $version;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getErrorCorrectionLevel()
+ {
+ return $this->level;
+ }
+
+ //----------------------------------------------------------------------
+ public function setErrorCorrectionLevel($level)
+ {
+ if($level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid ECLEVEL');
+ return -1;
+ }
+
+ $this->level = $level;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendEntry(QRinputItem $entry)
+ {
+ $this->items[] = $entry;
+ }
+
+ //----------------------------------------------------------------------
+ public function append($mode, $size, $data)
+ {
+ try {
+ $entry = new QRinputItem($mode, $size, $data);
+ $this->items[] = $entry;
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+
+ public function insertStructuredAppendHeader($size, $index, $parity)
+ {
+ if( $size > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong size');
+ }
+
+ if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong index');
+ }
+
+ $buf = array($size, $index, $parity);
+
+ try {
+ $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf);
+ array_unshift($this->items, $entry);
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function calcParity()
+ {
+ $parity = 0;
+
+ foreach($this->items as $item) {
+ if($item->mode != QR_MODE_STRUCTURE) {
+ for($i=$item->size-1; $i>=0; $i--) {
+ $parity ^= $item->data[$i];
+ }
+ }
+ }
+
+ return $parity;
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeNum($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeNum($size)
+ {
+ $w = (int)$size / 3;
+ $bits = $w * 10;
+
+ switch($size - $w * 3) {
+ case 1:
+ $bits += 4;
+ break;
+ case 2:
+ $bits += 7;
+ break;
+ default:
+ break;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static $anTable = array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ );
+
+ //----------------------------------------------------------------------
+ public static function lookAnTable($c)
+ {
+ return (($c > 127)?-1:self::$anTable[$c]);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeAn($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if (self::lookAnTable(ord($data[$i])) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeAn($size)
+ {
+ $w = (int)($size / 2);
+ $bits = $w * 11;
+
+ if($size & 1) {
+ $bits += 6;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsMode8($size)
+ {
+ return $size * 8;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitsModeKanji($size)
+ {
+ return (int)(($size / 2) * 13);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeKanji($size, $data)
+ {
+ if($size & 1)
+ return false;
+
+ for($i=0; $i<$size; $i+=2) {
+ $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
+ if( $val < 0x8140
+ || ($val > 0x9ffc && $val < 0xe040)
+ || $val > 0xebbf) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /***********************************************************************
+ * Validation
+ **********************************************************************/
+
+ public static function check($mode, $size, $data)
+ {
+ if($size <= 0)
+ return false;
+
+ switch($mode) {
+ case QR_MODE_NUM: return self::checkModeNum($size, $data); break;
+ case QR_MODE_AN: return self::checkModeAn($size, $data); break;
+ case QR_MODE_KANJI: return self::checkModeKanji($size, $data); break;
+ case QR_MODE_8: return true; break;
+ case QR_MODE_STRUCTURE: return true; break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSize($version)
+ {
+ $bits = 0;
+
+ foreach($this->items as $item) {
+ $bits += $item->estimateBitStreamSizeOfEntry($version);
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateVersion()
+ {
+ $version = 0;
+ $prev = 0;
+ do {
+ $prev = $version;
+ $bits = $this->estimateBitStreamSize($prev);
+ $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($version < 0) {
+ return -1;
+ }
+ } while ($version > $prev);
+
+ return $version;
+ }
+
+ //----------------------------------------------------------------------
+ public static function lengthOfCode($mode, $version, $bits)
+ {
+ $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);
+ switch($mode) {
+ case QR_MODE_NUM:
+ $chunks = (int)($payload / 10);
+ $remain = $payload - $chunks * 10;
+ $size = $chunks * 3;
+ if($remain >= 7) {
+ $size += 2;
+ } else if($remain >= 4) {
+ $size += 1;
+ }
+ break;
+ case QR_MODE_AN:
+ $chunks = (int)($payload / 11);
+ $remain = $payload - $chunks * 11;
+ $size = $chunks * 2;
+ if($remain >= 6)
+ $size++;
+ break;
+ case QR_MODE_8:
+ $size = (int)($payload / 8);
+ break;
+ case QR_MODE_KANJI:
+ $size = (int)(($payload / 13) * 2);
+ break;
+ case QR_MODE_STRUCTURE:
+ $size = (int)($payload / 8);
+ break;
+ default:
+ $size = 0;
+ break;
+ }
+
+ $maxsize = QRspec::maximumWords($mode, $version);
+ if($size < 0) $size = 0;
+ if($size > $maxsize) $size = $maxsize;
+
+ return $size;
+ }
+
+ //----------------------------------------------------------------------
+ public function createBitStream()
+ {
+ $total = 0;
+
+ foreach($this->items as $item) {
+ $bits = $item->encodeBitStream($this->version);
+
+ if($bits < 0)
+ return -1;
+
+ $total += $bits;
+ }
+
+ return $total;
+ }
+
+ //----------------------------------------------------------------------
+ public function convertData()
+ {
+ $ver = $this->estimateVersion();
+ if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ }
+
+ for(;;) {
+ $bits = $this->createBitStream();
+
+ if($bits < 0)
+ return -1;
+
+ $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if($ver < 0) {
+ throw new Exception('WRONG VERSION');
+ return -1;
+ } else if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ } else {
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendPaddingBit(&$bstream)
+ {
+ $bits = $bstream->size();
+ $maxwords = QRspec::getDataLength($this->version, $this->level);
+ $maxbits = $maxwords * 8;
+
+ if ($maxbits == $bits) {
+ return 0;
+ }
+
+ if ($maxbits - $bits < 5) {
+ return $bstream->appendNum($maxbits - $bits, 0);
+ }
+
+ $bits += 4;
+ $words = (int)(($bits + 7) / 8);
+
+ $padding = new QRbitstream();
+ $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
+
+ if($ret < 0)
+ return $ret;
+
+ $padlen = $maxwords - $words;
+
+ if($padlen > 0) {
+
+ $padbuf = array();
+ for($i=0; $i<$padlen; $i++) {
+ $padbuf[$i] = ($i&1)?0x11:0xec;
+ }
+
+ $ret = $padding->appendBytes($padlen, $padbuf);
+
+ if($ret < 0)
+ return $ret;
+
+ }
+
+ $ret = $bstream->append($padding);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function mergeBitStream()
+ {
+ if($this->convertData() < 0) {
+ return null;
+ }
+
+ $bstream = new QRbitstream();
+
+ foreach($this->items as $item) {
+ $ret = $bstream->append($item->bstream);
+ if($ret < 0) {
+ return null;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getBitStream()
+ {
+
+ $bstream = $this->mergeBitStream();
+
+ if($bstream == null) {
+ return null;
+ }
+
+ $ret = $this->appendPaddingBit($bstream);
+ if($ret < 0) {
+ return null;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getByteStream()
+ {
+ $bstream = $this->getBitStream();
+ if($bstream == null) {
+ return null;
+ }
+
+ return $bstream->toByte();
+ }
+ }
+
+
+
+
+
+
+//---- qrbitstream.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Bitstream class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRbitstream {
+
+ public $data = array();
+
+ //----------------------------------------------------------------------
+ public function size()
+ {
+ return count($this->data);
+ }
+
+ //----------------------------------------------------------------------
+ public function allocate($setLength)
+ {
+ $this->data = array_fill(0, $setLength, 0);
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromNum($bits, $num)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($bits);
+
+ $mask = 1 << ($bits - 1);
+ for($i=0; $i<$bits; $i++) {
+ if($num & $mask) {
+ $bstream->data[$i] = 1;
+ } else {
+ $bstream->data[$i] = 0;
+ }
+ $mask = $mask >> 1;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromBytes($size, $data)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($size * 8);
+ $p=0;
+
+ for($i=0; $i<$size; $i++) {
+ $mask = 0x80;
+ for($j=0; $j<8; $j++) {
+ if($data[$i] & $mask) {
+ $bstream->data[$p] = 1;
+ } else {
+ $bstream->data[$p] = 0;
+ }
+ $p++;
+ $mask = $mask >> 1;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function append(QRbitstream $arg)
+ {
+ if (is_null($arg)) {
+ return -1;
+ }
+
+ if($arg->size() == 0) {
+ return 0;
+ }
+
+ if($this->size() == 0) {
+ $this->data = $arg->data;
+ return 0;
+ }
+
+ $this->data = array_values(array_merge($this->data, $arg->data));
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendNum($bits, $num)
+ {
+ if ($bits == 0)
+ return 0;
+
+ $b = QRbitstream::newFromNum($bits, $num);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendBytes($size, $data)
+ {
+ if ($size == 0)
+ return 0;
+
+ $b = QRbitstream::newFromBytes($size, $data);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function toByte()
+ {
+
+ $size = $this->size();
+
+ if($size == 0) {
+ return array();
+ }
+
+ $data = array_fill(0, (int)(($size + 7) / 8), 0);
+ $bytes = (int)($size / 8);
+
+ $p = 0;
+
+ for($i=0; $i<$bytes; $i++) {
+ $v = 0;
+ for($j=0; $j<8; $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$i] = $v;
+ }
+
+ if($size & 7) {
+ $v = 0;
+ for($j=0; $j<($size & 7); $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$bytes] = $v;
+ }
+
+ return $data;
+ }
+
+ }
+
+
+
+
+//---- qrsplit.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Input splitting classes
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+ class QRsplit {
+
+ public $dataStr = '';
+ public $input;
+ public $modeHint;
+
+ //----------------------------------------------------------------------
+ public function __construct($dataStr, $input, $modeHint)
+ {
+ $this->dataStr = $dataStr;
+ $this->input = $input;
+ $this->modeHint = $modeHint;
+ }
+
+ //----------------------------------------------------------------------
+ public static function isdigitat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
+ }
+
+ //----------------------------------------------------------------------
+ public static function isalnumat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return (QRinput::lookAnTable(ord($str[$pos])) >= 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function identifyMode($pos)
+ {
+ if ($pos >= strlen($this->dataStr))
+ return QR_MODE_NUL;
+
+ $c = $this->dataStr[$pos];
+
+ if(self::isdigitat($this->dataStr, $pos)) {
+ return QR_MODE_NUM;
+ } else if(self::isalnumat($this->dataStr, $pos)) {
+ return QR_MODE_AN;
+ } else if($this->modeHint == QR_MODE_KANJI) {
+
+ if ($pos+1 < strlen($this->dataStr))
+ {
+ $d = $this->dataStr[$pos+1];
+ $word = (ord($c) << 8) | ord($d);
+ if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
+ return QR_MODE_KANJI;
+ }
+ }
+ }
+
+ return QR_MODE_8;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatNum()
+ {
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+ while(self::isdigitat($this->dataStr, $p)) {
+ $p++;
+ }
+
+ $run = $p;
+ $mode = $this->identifyMode($p);
+
+ if($mode == QR_MODE_8) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ if($mode == QR_MODE_AN) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsModeAn(1) // + 4 + la
+ - QRinput::estimateBitsModeAn($run + 1);// - 4 - la
+ if($dif > 0) {
+ return $this->eatAn();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_NUM, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatAn()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+
+ while(self::isalnumat($this->dataStr, $p)) {
+ if(self::isdigitat($this->dataStr, $p)) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+
+ $dif = QRinput::estimateBitsModeAn($p) // + 4 + la
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsModeAn($q); // - 4 - la
+
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+
+ if(!self::isalnumat($this->dataStr, $p)) {
+ $dif = QRinput::estimateBitsModeAn($run) + 4 + $la
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_AN, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatKanji()
+ {
+ $p = 0;
+
+ while($this->identifyMode($p) == QR_MODE_KANJI) {
+ $p += 2;
+ }
+
+ $ret = $this->input->append(QR_MODE_KANJI, $p, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eat8()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 1;
+ $dataStrLen = strlen($this->dataStr);
+
+ while($p < $dataStrLen) {
+
+ $mode = $this->identifyMode($p);
+ if($mode == QR_MODE_KANJI) {
+ break;
+ }
+ if($mode == QR_MODE_NUM) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else if($mode == QR_MODE_AN) {
+ $q = $p;
+ while(self::isalnumat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeAn($q - $p) + 4 + $la
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+ $ret = $this->input->append(QR_MODE_8, $run, str_split($this->dataStr));
+
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function splitString()
+ {
+ while (strlen($this->dataStr) > 0)
+ {
+ if($this->dataStr == '')
+ return 0;
+
+ $mode = $this->identifyMode(0);
+
+ switch ($mode) {
+ case QR_MODE_NUM: $length = $this->eatNum(); break;
+ case QR_MODE_AN: $length = $this->eatAn(); break;
+ case QR_MODE_KANJI:
+ if ($hint == QR_MODE_KANJI)
+ $length = $this->eatKanji();
+ else $length = $this->eat8();
+ break;
+ default: $length = $this->eat8(); break;
+
+ }
+
+ if($length == 0) return 0;
+ if($length < 0) return -1;
+
+ $this->dataStr = substr($this->dataStr, $length);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function toUpper()
+ {
+ $stringLen = strlen($this->dataStr);
+ $p = 0;
+
+ while ($p<$stringLen) {
+ $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint);
+ if($mode == QR_MODE_KANJI) {
+ $p += 2;
+ } else {
+ if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) {
+ $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
+ }
+ $p++;
+ }
+ }
+
+ return $this->dataStr;
+ }
+
+ //----------------------------------------------------------------------
+ public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true)
+ {
+ if(is_null($string) || $string == '\0' || $string == '') {
+ throw new Exception('empty string!!!');
+ }
+
+ $split = new QRsplit($string, $input, $modeHint);
+
+ if(!$casesensitive)
+ $split->toUpper();
+
+ return $split->splitString();
+ }
+ }
+
+
+
+//---- qrrscode.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Reed-Solomon error correction support
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q
+ * (libfec is released under the GNU Lesser General Public License.)
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsItem {
+
+ public $mm; // Bits per symbol
+ public $nn; // Symbols per block (= (1<= $this->nn) {
+ $x -= $this->nn;
+ $x = ($x >> $this->mm) + ($x & $this->nn);
+ }
+
+ return $x;
+ }
+
+ //----------------------------------------------------------------------
+ public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ // Common code for intializing a Reed-Solomon control block (char or int symbols)
+ // Copyright 2004 Phil Karn, KA9Q
+ // May be used under the terms of the GNU Lesser General Public License (LGPL)
+
+ $rs = null;
+
+ // Check parameter ranges
+ if($symsize < 0 || $symsize > 8) return $rs;
+ if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs;
+ if($prim <= 0 || $prim >= (1<<$symsize)) return $rs;
+ if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values!
+ if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding
+
+ $rs = new QRrsItem();
+ $rs->mm = $symsize;
+ $rs->nn = (1<<$symsize)-1;
+ $rs->pad = $pad;
+
+ $rs->alpha_to = array_fill(0, $rs->nn+1, 0);
+ $rs->index_of = array_fill(0, $rs->nn+1, 0);
+
+ // PHP style macro replacement ;)
+ $NN =& $rs->nn;
+ $A0 =& $NN;
+
+ // Generate Galois field lookup tables
+ $rs->index_of[0] = $A0; // log(zero) = -inf
+ $rs->alpha_to[$A0] = 0; // alpha**-inf = 0
+ $sr = 1;
+
+ for($i=0; $i<$rs->nn; $i++) {
+ $rs->index_of[$sr] = $i;
+ $rs->alpha_to[$i] = $sr;
+ $sr <<= 1;
+ if($sr & (1<<$symsize)) {
+ $sr ^= $gfpoly;
+ }
+ $sr &= $rs->nn;
+ }
+
+ if($sr != 1){
+ // field generator polynomial is not primitive!
+ $rs = NULL;
+ return $rs;
+ }
+
+ /* Form RS code generator polynomial from its roots */
+ $rs->genpoly = array_fill(0, $nroots+1, 0);
+
+ $rs->fcr = $fcr;
+ $rs->prim = $prim;
+ $rs->nroots = $nroots;
+ $rs->gfpoly = $gfpoly;
+
+ /* Find prim-th root of 1, used in decoding */
+ for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn)
+ ; // intentional empty-body loop!
+
+ $rs->iprim = (int)($iprim / $prim);
+ $rs->genpoly[0] = 1;
+
+ for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
+ $rs->genpoly[$i+1] = 1;
+
+ // Multiply rs->genpoly[] by @**(root + x)
+ for ($j = $i; $j > 0; $j--) {
+ if ($rs->genpoly[$j] != 0) {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)];
+ } else {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1];
+ }
+ }
+ // rs->genpoly[0] can never be zero
+ $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)];
+ }
+
+ // convert rs->genpoly[] to index form for quicker encoding
+ for ($i = 0; $i <= $nroots; $i++)
+ $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]];
+
+ return $rs;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode_rs_char($data, &$parity)
+ {
+ $MM =& $this->mm;
+ $NN =& $this->nn;
+ $ALPHA_TO =& $this->alpha_to;
+ $INDEX_OF =& $this->index_of;
+ $GENPOLY =& $this->genpoly;
+ $NROOTS =& $this->nroots;
+ $FCR =& $this->fcr;
+ $PRIM =& $this->prim;
+ $IPRIM =& $this->iprim;
+ $PAD =& $this->pad;
+ $A0 =& $NN;
+
+ $parity = array_fill(0, $NROOTS, 0);
+
+ for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) {
+
+ $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
+ if($feedback != $A0) {
+ // feedback term is non-zero
+
+ // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
+ // always be for the polynomials constructed by init_rs()
+ $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback);
+
+ for($j=1;$j<$NROOTS;$j++) {
+ $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS-$j])];
+ }
+ }
+
+ // Shift
+ array_shift($parity);
+ if($feedback != $A0) {
+ array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]);
+ } else {
+ array_push($parity, 0);
+ }
+ }
+ }
+ }
+
+ //##########################################################################
+
+ class QRrs {
+
+ public static $items = array();
+
+ //----------------------------------------------------------------------
+ public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ foreach(self::$items as $rs) {
+ if($rs->pad != $pad) continue;
+ if($rs->nroots != $nroots) continue;
+ if($rs->mm != $symsize) continue;
+ if($rs->gfpoly != $gfpoly) continue;
+ if($rs->fcr != $fcr) continue;
+ if($rs->prim != $prim) continue;
+
+ return $rs;
+ }
+
+ $rs = QRrsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
+ array_unshift(self::$items, $rs);
+
+ return $rs;
+ }
+ }
+
+
+
+//---- qrmask.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Masking
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('N1', 3);
+ define('N2', 3);
+ define('N3', 40);
+ define('N4', 10);
+
+ class QRmask {
+
+ public $runLength = array();
+
+ //----------------------------------------------------------------------
+ public function __construct()
+ {
+ $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function writeFormatInformation($width, &$frame, $mask, $level)
+ {
+ $blacks = 0;
+ $format = QRspec::getFormatInfo($mask, $level);
+
+ for($i=0; $i<8; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[8][$width - 1 - $i] = chr($v);
+ if($i < 6) {
+ $frame[$i][8] = chr($v);
+ } else {
+ $frame[$i + 1][8] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+
+ for($i=0; $i<7; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[$width - 7 + $i][8] = chr($v);
+ if($i == 0) {
+ $frame[8][7] = chr($v);
+ } else {
+ $frame[8][6 - $i] = chr($v);
+ }
+
+ $format = $format >> 1;
+ }
+
+ return $blacks;
+ }
+
+ //----------------------------------------------------------------------
+ public function mask0($x, $y) { return ($x+$y)&1; }
+ public function mask1($x, $y) { return ($y&1); }
+ public function mask2($x, $y) { return ($x%3); }
+ public function mask3($x, $y) { return ($x+$y)%3; }
+ public function mask4($x, $y) { return (((int)($y/2))+((int)($x/3)))&1; }
+ public function mask5($x, $y) { return (($x*$y)&1)+($x*$y)%3; }
+ public function mask6($x, $y) { return ((($x*$y)&1)+($x*$y)%3)&1; }
+ public function mask7($x, $y) { return ((($x*$y)%3)+(($x+$y)&1))&1; }
+
+ //----------------------------------------------------------------------
+ private function generateMaskNo($maskNo, $width, $frame)
+ {
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if(ord($frame[$y][$x]) & 0x80) {
+ $bitMask[$y][$x] = 0;
+ } else {
+ $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
+ $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
+ }
+
+ }
+ }
+
+ return $bitMask;
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($bitFrame)
+ {
+ $codeArr = array();
+
+ foreach ($bitFrame as $line)
+ $codeArr[] = join('', $line);
+
+ return gzcompress(join("\n", $codeArr), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ $codeArr = array();
+
+ $codeLines = explode("\n", gzuncompress($code));
+ foreach ($codeLines as $line)
+ $codeArr[] = str_split($line);
+
+ return $codeArr;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false)
+ {
+ $b = 0;
+ $bitMask = array();
+
+ $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ $bitMask = self::unserial(file_get_contents($fileName));
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo))
+ mkdir(QR_CACHE_DIR.'mask_'.$maskNo);
+ file_put_contents($fileName, self::serial($bitMask));
+ }
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ }
+
+ if ($maskGenOnly)
+ return;
+
+ $d = $s;
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if($bitMask[$y][$x] == 1) {
+ $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
+ }
+ $b += (int)(ord($d[$y][$x]) & 1);
+ }
+ }
+
+ return $b;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMask($width, $frame, $maskNo, $level)
+ {
+ $masked = array_fill(0, $width, str_repeat("\0", $width));
+ $this->makeMaskNo($maskNo, $width, $frame, $masked);
+ $this->writeFormatInformation($width, $masked, $maskNo, $level);
+
+ return $masked;
+ }
+
+ //----------------------------------------------------------------------
+ public function calcN1N3($length)
+ {
+ $demerit = 0;
+
+ for($i=0; $i<$length; $i++) {
+
+ if($this->runLength[$i] >= 5) {
+ $demerit += (N1 + ($this->runLength[$i] - 5));
+ }
+ if($i & 1) {
+ if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) {
+ $fact = (int)($this->runLength[$i] / 3);
+ if(($this->runLength[$i-2] == $fact) &&
+ ($this->runLength[$i-1] == $fact) &&
+ ($this->runLength[$i+1] == $fact) &&
+ ($this->runLength[$i+2] == $fact)) {
+ if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) {
+ $demerit += N3;
+ } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) {
+ $demerit += N3;
+ }
+ }
+ }
+ }
+ }
+ return $demerit;
+ }
+
+ //----------------------------------------------------------------------
+ public function evaluateSymbol($width, $frame)
+ {
+ $head = 0;
+ $demerit = 0;
+
+ for($y=0; $y<$width; $y++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ $frameY = $frame[$y];
+
+ if ($y>0)
+ $frameYM = $frame[$y-1];
+
+ for($x=0; $x<$width; $x++) {
+ if(($x > 0) && ($y > 0)) {
+ $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
+ $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
+
+ if(($b22 | ($w22 ^ 1))&1) {
+ $demerit += N2;
+ }
+ }
+ if(($x == 0) && (ord($frameY[$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($x > 0) {
+ if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ for($x=0; $x<$width; $x++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ for($y=0; $y<$width; $y++) {
+ if($y == 0 && (ord($frame[$y][$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($y > 0) {
+ if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ return $demerit;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function mask($width, $frame, $level)
+ {
+ $minDemerit = PHP_INT_MAX;
+ $bestMaskNum = 0;
+ $bestMask = array();
+
+ $checked_masks = array(0,1,2,3,4,5,6,7);
+
+ if (QR_FIND_FROM_RANDOM !== false) {
+
+ $howManuOut = 8-(QR_FIND_FROM_RANDOM % 9);
+ for ($i = 0; $i < $howManuOut; $i++) {
+ $remPos = rand (0, count($checked_masks)-1);
+ unset($checked_masks[$remPos]);
+ $checked_masks = array_values($checked_masks);
+ }
+
+ }
+
+ $bestMask = $frame;
+
+ foreach($checked_masks as $i) {
+ $mask = array_fill(0, $width, str_repeat("\0", $width));
+
+ $demerit = 0;
+ $blacks = 0;
+ $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
+ $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
+ $blacks = (int)(100 * $blacks / ($width * $width));
+ $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
+ $demerit += $this->evaluateSymbol($width, $mask);
+
+ if($demerit < $minDemerit) {
+ $minDemerit = $demerit;
+ $bestMask = $mask;
+ $bestMaskNum = $i;
+ }
+ }
+
+ return $bestMask;
+ }
+
+ //----------------------------------------------------------------------
+ }
+
+
+
+
+//---- qrencode.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Main encoder classes.
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsblock {
+ public $dataLength;
+ public $data = array();
+ public $eccLength;
+ public $ecc = array();
+
+ public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs)
+ {
+ $rs->encode_rs_char($data, $ecc);
+
+ $this->dataLength = $dl;
+ $this->data = $data;
+ $this->eccLength = $el;
+ $this->ecc = $ecc;
+ }
+ };
+
+ //##########################################################################
+
+ class QRrawcode {
+ public $version;
+ public $datacode = array();
+ public $ecccode = array();
+ public $blocks;
+ public $rsblocks = array(); //of RSblock
+ public $count;
+ public $dataLength;
+ public $eccLength;
+ public $b1;
+
+ //----------------------------------------------------------------------
+ public function __construct(QRinput $input)
+ {
+ $spec = array(0,0,0,0,0);
+
+ $this->datacode = $input->getByteStream();
+ if(is_null($this->datacode)) {
+ throw new Exception('null imput string');
+ }
+
+ QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec);
+
+ $this->version = $input->getVersion();
+ $this->b1 = QRspec::rsBlockNum1($spec);
+ $this->dataLength = QRspec::rsDataLength($spec);
+ $this->eccLength = QRspec::rsEccLength($spec);
+ $this->ecccode = array_fill(0, $this->eccLength, 0);
+ $this->blocks = QRspec::rsBlockNum($spec);
+
+ $ret = $this->init($spec);
+ if($ret < 0) {
+ throw new Exception('block alloc error');
+ return null;
+ }
+
+ $this->count = 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function init(array $spec)
+ {
+ $dl = QRspec::rsDataCodes1($spec);
+ $el = QRspec::rsEccCodes1($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+
+ $blockNo = 0;
+ $dataPos = 0;
+ $eccPos = 0;
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ if(QRspec::rsBlockNum2($spec) == 0)
+ return 0;
+
+ $dl = QRspec::rsDataCodes2($spec);
+ $el = QRspec::rsEccCodes2($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+ if($rs == NULL) return -1;
+
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getCode()
+ {
+ $ret;
+
+ if($this->count < $this->dataLength) {
+ $row = $this->count % $this->blocks;
+ $col = $this->count / $this->blocks;
+ if($col >= $this->rsblocks[0]->dataLength) {
+ $row += $this->b1;
+ }
+ $ret = $this->rsblocks[$row]->data[$col];
+ } else if($this->count < $this->dataLength + $this->eccLength) {
+ $row = ($this->count - $this->dataLength) % $this->blocks;
+ $col = ($this->count - $this->dataLength) / $this->blocks;
+ $ret = $this->rsblocks[$row]->ecc[$col];
+ } else {
+ return 0;
+ }
+ $this->count++;
+
+ return $ret;
+ }
+ }
+
+ //##########################################################################
+
+ class QRcode {
+
+ public $version;
+ public $width;
+ public $data;
+
+ //----------------------------------------------------------------------
+ public function encodeMask(QRinput $input, $mask)
+ {
+ if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) {
+ throw new Exception('wrong version');
+ }
+ if($input->getErrorCorrectionLevel() > QR_ECLEVEL_H) {
+ throw new Exception('wrong level');
+ }
+
+ $raw = new QRrawcode($input);
+
+ QRtools::markTime('after_raw');
+
+ $version = $raw->version;
+ $width = QRspec::getWidth($version);
+ $frame = QRspec::newFrame($version);
+
+ $filler = new FrameFiller($width, $frame);
+ if(is_null($filler)) {
+ return NULL;
+ }
+
+ // inteleaved data and ecc codes
+ for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) {
+ $code = $raw->getCode();
+ $bit = 0x80;
+ for($j=0; $j<8; $j++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
+ $bit = $bit >> 1;
+ }
+ }
+
+ QRtools::markTime('after_filler');
+
+ unset($raw);
+
+ // remainder bits
+ $j = QRspec::getRemainder($version);
+ for($i=0; $i<$j; $i++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02);
+ }
+
+ $frame = $filler->frame;
+ unset($filler);
+
+
+ // masking
+ $maskObj = new QRmask();
+ if($mask < 0) {
+
+ if (QR_FIND_BEST_MASK) {
+ $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, (intval(QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel());
+ }
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());
+ }
+
+ if($masked == NULL) {
+ return NULL;
+ }
+
+ QRtools::markTime('after_mask');
+
+ $this->version = $version;
+ $this->width = $width;
+ $this->data = $masked;
+
+ return $this;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeInput(QRinput $input)
+ {
+ return $this->encodeMask($input, -1);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString8bit($string, $version, $level)
+ {
+ if(string == NULL) {
+ throw new Exception('empty string!');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = $input->append($input, QR_MODE_8, strlen($string), str_split($string));
+ if($ret < 0) {
+ unset($input);
+ return NULL;
+ }
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString($string, $version, $level, $hint, $casesensitive)
+ {
+
+ if($hint != QR_MODE_8 && $hint != QR_MODE_KANJI) {
+ throw new Exception('bad hint');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive);
+ if($ret < 0) {
+ return NULL;
+ }
+
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public static function png($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodePNG($text, $outfile, $saveandprint=false);
+ }
+
+ //----------------------------------------------------------------------
+ public static function text($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encode($text, $outfile);
+ }
+
+ //----------------------------------------------------------------------
+ public static function raw($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodeRAW($text, $outfile);
+ }
+ }
+
+ //##########################################################################
+
+ class FrameFiller {
+
+ public $width;
+ public $frame;
+ public $x;
+ public $y;
+ public $dir;
+ public $bit;
+
+ //----------------------------------------------------------------------
+ public function __construct($width, &$frame)
+ {
+ $this->width = $width;
+ $this->frame = $frame;
+ $this->x = $width - 1;
+ $this->y = $width - 1;
+ $this->dir = -1;
+ $this->bit = -1;
+ }
+
+ //----------------------------------------------------------------------
+ public function setFrameAt($at, $val)
+ {
+ $this->frame[$at['y']][$at['x']] = chr($val);
+ }
+
+ //----------------------------------------------------------------------
+ public function getFrameAt($at)
+ {
+ return ord($this->frame[$at['y']][$at['x']]);
+ }
+
+ //----------------------------------------------------------------------
+ public function next()
+ {
+ do {
+
+ if($this->bit == -1) {
+ $this->bit = 0;
+ return array('x'=>$this->x, 'y'=>$this->y);
+ }
+
+ $x = $this->x;
+ $y = $this->y;
+ $w = $this->width;
+
+ if($this->bit == 0) {
+ $x--;
+ $this->bit++;
+ } else {
+ $x++;
+ $y += $this->dir;
+ $this->bit--;
+ }
+
+ if($this->dir < 0) {
+ if($y < 0) {
+ $y = 0;
+ $x -= 2;
+ $this->dir = 1;
+ if($x == 6) {
+ $x--;
+ $y = 9;
+ }
+ }
+ } else {
+ if($y == $w) {
+ $y = $w - 1;
+ $x -= 2;
+ $this->dir = -1;
+ if($x == 6) {
+ $x--;
+ $y -= 8;
+ }
+ }
+ }
+ if($x < 0 || $y < 0) return null;
+
+ $this->x = $x;
+ $this->y = $y;
+
+ } while(ord($this->frame[$y][$x]) & 0x80);
+
+ return array('x'=>$x, 'y'=>$y);
+ }
+
+ } ;
+
+ //##########################################################################
+
+ class QRencode {
+
+ public $casesensitive = true;
+ public $eightbit = false;
+
+ public $version = 0;
+ public $size = 3;
+ public $margin = 4;
+
+ public $structured = 0; // not supported yet
+
+ public $level = QR_ECLEVEL_L;
+ public $hint = QR_MODE_8;
+
+ //----------------------------------------------------------------------
+ public static function factory($level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = new QRencode();
+ $enc->size = $size;
+ $enc->margin = $margin;
+
+ switch ($level.'') {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ $enc->level = $level;
+ break;
+ case 'l':
+ case 'L':
+ $enc->level = QR_ECLEVEL_L;
+ break;
+ case 'm':
+ case 'M':
+ $enc->level = QR_ECLEVEL_M;
+ break;
+ case 'q':
+ case 'Q':
+ $enc->level = QR_ECLEVEL_Q;
+ break;
+ case 'h':
+ case 'H':
+ $enc->level = QR_ECLEVEL_H;
+ break;
+ }
+
+ return $enc;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeRAW($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ return $code->data;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ QRtools::markTime('after_encode');
+
+ if ($outfile!== false) {
+ file_put_contents($outfile, join("\n", QRtools::binarize($code->data)));
+ } else {
+ return QRtools::binarize($code->data);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodePNG($intext, $outfile = false,$saveandprint=false)
+ {
+ try {
+
+ ob_start();
+ $tab = $this->encode($intext);
+ $err = ob_get_contents();
+ ob_end_clean();
+
+ if ($err != '')
+ QRtools::log($outfile, $err);
+
+ $maxSize = (int)(QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin));
+
+ QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint);
+
+ } catch (Exception $e) {
+
+ QRtools::log($outfile, $e->getMessage());
+
+ }
+ }
+ }
+
+}
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/lib/phpqrcode/CHANGELOG b/app/lib/phpqrcode/CHANGELOG
new file mode 100644
index 00000000..1088530c
--- /dev/null
+++ b/app/lib/phpqrcode/CHANGELOG
@@ -0,0 +1,38 @@
+* 1.0.0 build 2010031920
+
+ - first public release
+ - help in readme, install
+ - cleanup ans separation of QRtools and QRspec
+ - now TCPDF binding requires minimal changes in TCPDF, having most of job
+ done in QRtools tcpdfBarcodeArray
+ - nicer QRtools::timeBenchmark output
+ - license and copyright notices in files
+ - indent cleanup - from tab to 4spc, keep it that way please :)
+ - sf project, repository, wiki
+ - simple code generator in index.php
+
+* 1.1.0 build 2010032113
+
+ - added merge tool wich generate merged version of code
+ located in phpqrcode.php
+ - splited qrconst.php from qrlib.php
+
+* 1.1.1 build 2010032405
+
+ - patch by Rick Seymour allowing saving PNG and displaying it at the same time
+ - added version info in VERSION file
+ - modified merge tool to include version info into generated file
+ - fixed e-mail in almost all head comments
+
+* 1.1.2 build 2010032722
+
+ - full integration with TCPDF thanks to Nicola Asuni, it's author
+ - fixed bug with alphanumeric encoding detection
+
+* 1.1.3 build 2010081807
+
+ - short opening tags replaced with standard ones
+
+* 1.1.4 build 2010100721
+
+ - added missing static keyword QRinput::check (found by Luke Brookhart, Onjax LLC)
diff --git a/app/lib/phpqrcode/INSTALL b/app/lib/phpqrcode/INSTALL
new file mode 100644
index 00000000..eac6b072
--- /dev/null
+++ b/app/lib/phpqrcode/INSTALL
@@ -0,0 +1,67 @@
+== REQUIREMENTS ==
+
+ * PHP5
+ * PHP GD2 extension with JPEG and PNG support
+
+== INSTALLATION ==
+
+If you want to recreate cache by yourself make sure cache directory is
+writable and you have permisions to write into it. Also make sure you are
+able to read files in it if you have cache option enabled
+
+== CONFIGURATION ==
+
+Feel free to modify config constants in qrconfig.php file. Read about it in
+provided comments and project wiki page (links in README file)
+
+== QUICK START ==
+
+Notice: probably you should'nt use all of this in same script :)
+
+encode('PHP QR Code :)');
+QRspec::debug($tab, true);
+
+== TCPDF INTEGRATION ==
+
+Inside bindings/tcpdf you will find slightly modified 2dbarcodes.php.
+Instal phpqrcode liblaty inside tcpdf folder, then overwrite (or merge)
+2dbarcodes.php
+
+Then use similar as example #50 from TCPDF examples:
+
+ true,
+ 'padding' => 4,
+ 'fgcolor' => array(0,0,0),
+ 'bgcolor' => false, //array(255,255,255)
+);
+
+//code name: QR, specify error correction level after semicolon (L,M,Q,H)
+$pdf->write2DBarcode('PHP QR Code :)', 'QR,L', '', '', 30, 30, $style, 'N');
diff --git a/app/lib/phpqrcode/LICENSE b/app/lib/phpqrcode/LICENSE
new file mode 100644
index 00000000..18833032
--- /dev/null
+++ b/app/lib/phpqrcode/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/app/lib/phpqrcode/README b/app/lib/phpqrcode/README
new file mode 100644
index 00000000..a022fb5e
--- /dev/null
+++ b/app/lib/phpqrcode/README
@@ -0,0 +1,45 @@
+This is PHP implementation of QR Code 2-D barcode generator. It is pure-php
+LGPL-licensed implementation based on C libqrencode by Kentaro Fukuchi.
+
+== LICENSING ==
+
+Copyright (C) 2010 by Dominik Dzienia
+
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free
+Software Foundation; either version 3 of the License, or any later version.
+
+This library is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License (LICENSE file)
+for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this library; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+== INSTALATION AND USAGE ==
+
+ * INSTALL file
+ * http://sourceforge.net/apps/mediawiki/phpqrcode/index.php?title=Main_Page
+
+== CONTACT ==
+
+Fell free to contact me via e-mail (deltalab at poczta dot fm) or using
+folowing project pages:
+
+ * http://sourceforge.net/projects/phpqrcode/
+ * http://phpqrcode.sourceforge.net/
+
+== ACKNOWLEDGMENTS ==
+
+Based on C libqrencode library (ver. 3.1.1)
+Copyright (C) 2006-2010 by Kentaro Fukuchi
+http://megaui.net/fukuchi/works/qrencode/index.en.html
+
+QR Code is registered trademarks of DENSO WAVE INCORPORATED in JAPAN and other
+countries.
+
+Reed-Solomon code encoder is written by Phil Karn, KA9Q.
+Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q
+
\ No newline at end of file
diff --git a/app/lib/phpqrcode/VERSION b/app/lib/phpqrcode/VERSION
new file mode 100644
index 00000000..9f99279e
--- /dev/null
+++ b/app/lib/phpqrcode/VERSION
@@ -0,0 +1,2 @@
+1.1.4
+2010100721
\ No newline at end of file
diff --git a/app/lib/phpqrcode/bindings/tcpdf/qrcode.php b/app/lib/phpqrcode/bindings/tcpdf/qrcode.php
new file mode 100644
index 00000000..7995460b
--- /dev/null
+++ b/app/lib/phpqrcode/bindings/tcpdf/qrcode.php
@@ -0,0 +1,2875 @@
+
+// http://phpqrcode.sourceforge.net/
+// https://sourceforge.net/projects/phpqrcode/
+//
+// The "PHP QR Code encoder" is based on
+// "C libqrencode library" (ver. 3.1.1)
+// License: GNU-LGPL 2.1
+// Copyright (C) 2006-2010 by Kentaro Fukuchi
+// http://megaui.net/fukuchi/works/qrencode/index.en.html
+//
+// Reed-Solomon code encoder is written by Phil Karn, KA9Q.
+// Copyright (C) 2002-2006 Phil Karn, KA9Q
+//
+// QR Code is registered trademark of DENSO WAVE INCORPORATED
+// http://www.denso-wave.com/qrcode/index-e.html
+// ---------------------------------------------------------
+//
+// Author: Nicola Asuni
+//
+// (c) Copyright 2010:
+// Nicola Asuni
+// Tecnick.com S.r.l.
+// Via della Pace, 11
+// 09044 Quartucciu (CA)
+// ITALY
+// www.tecnick.com
+// info@tecnick.com
+//============================================================+
+
+/**
+ * Class to create QR-code arrays for TCPDF class.
+ * QR Code symbol is a 2D barcode that can be scanned by handy terminals such as a mobile phone with CCD.
+ * The capacity of QR Code is up to 7000 digits or 4000 characters, and has high robustness.
+ * This class supports QR Code model 2, described in JIS (Japanese Industrial Standards) X0510:2004 or ISO/IEC 18004.
+ * Currently the following features are not supported: ECI and FNC1 mode, Micro QR Code, QR Code model 1, Structured mode.
+ *
+ * This class is derived from "PHP QR Code encoder" by Dominik Dzienia (http://phpqrcode.sourceforge.net/) based on "libqrencode C library 3.1.1." by Kentaro Fukuchi (http://megaui.net/fukuchi/works/qrencode/index.en.html), contains Reed-Solomon code written by Phil Karn, KA9Q. QR Code is registered trademark of DENSO WAVE INCORPORATED (http://www.denso-wave.com/qrcode/index-e.html).
+ * Please read comments on this class source file for full copyright and license information.
+ *
+ * @package com.tecnick.tcpdf
+ * @abstract Class for generating QR-code array for TCPDF.
+ * @author Nicola Asuni
+ * @copyright 2010 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
+ * @link http://www.tcpdf.org
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ * @version 1.0.002
+ */
+
+// definitions
+if (!defined('QRCODEDEFS')) {
+
+ /**
+ * Indicate that definitions for this class are set
+ */
+ define('QRCODEDEFS', true);
+
+ // -----------------------------------------------------
+
+ // Encoding modes (characters which can be encoded in QRcode)
+
+ /**
+ * Encoding mode
+ */
+ define('QR_MODE_NL', -1);
+
+ /**
+ * Encoding mode numeric (0-9). 3 characters are encoded to 10bit length. In theory, 7089 characters or less can be stored in a QRcode.
+ */
+ define('QR_MODE_NM', 0);
+
+ /**
+ * Encoding mode alphanumeric (0-9A-Z $%*+-./:) 45characters. 2 characters are encoded to 11bit length. In theory, 4296 characters or less can be stored in a QRcode.
+ */
+ define('QR_MODE_AN', 1);
+
+ /**
+ * Encoding mode 8bit byte data. In theory, 2953 characters or less can be stored in a QRcode.
+ */
+ define('QR_MODE_8B', 2);
+
+ /**
+ * Encoding mode KANJI. A KANJI character (multibyte character) is encoded to 13bit length. In theory, 1817 characters or less can be stored in a QRcode.
+ */
+ define('QR_MODE_KJ', 3);
+
+ /**
+ * Encoding mode STRUCTURED (currently unsupported)
+ */
+ define('QR_MODE_ST', 4);
+
+ // -----------------------------------------------------
+
+ // Levels of error correction.
+ // QRcode has a function of an error correcting for miss reading that white is black.
+ // Error correcting is defined in 4 level as below.
+
+ /**
+ * Error correction level L : About 7% or less errors can be corrected.
+ */
+ define('QR_ECLEVEL_L', 0);
+
+ /**
+ * Error correction level M : About 15% or less errors can be corrected.
+ */
+ define('QR_ECLEVEL_M', 1);
+
+ /**
+ * Error correction level Q : About 25% or less errors can be corrected.
+ */
+ define('QR_ECLEVEL_Q', 2);
+
+ /**
+ * Error correction level H : About 30% or less errors can be corrected.
+ */
+ define('QR_ECLEVEL_H', 3);
+
+ // -----------------------------------------------------
+
+ // Version. Size of QRcode is defined as version.
+ // Version is from 1 to 40.
+ // Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases.
+ // So version 40 is 177*177 matrix.
+
+ /**
+ * Maximum QR Code version.
+ */
+ define('QRSPEC_VERSION_MAX', 40);
+
+ /**
+ * Maximum matrix size for maximum version (version 40 is 177*177 matrix).
+ */
+ define('QRSPEC_WIDTH_MAX', 177);
+
+ // -----------------------------------------------------
+
+ /**
+ * Matrix index to get width from $capacity array.
+ */
+ define('QRCAP_WIDTH', 0);
+
+ /**
+ * Matrix index to get number of words from $capacity array.
+ */
+ define('QRCAP_WORDS', 1);
+
+ /**
+ * Matrix index to get remainder from $capacity array.
+ */
+ define('QRCAP_REMINDER', 2);
+
+ /**
+ * Matrix index to get error correction level from $capacity array.
+ */
+ define('QRCAP_EC', 3);
+
+ // -----------------------------------------------------
+
+ // Structure (currently usupported)
+
+ /**
+ * Number of header bits for structured mode
+ */
+ define('STRUCTURE_HEADER_BITS', 20);
+
+ /**
+ * Max number of symbols for structured mode
+ */
+ define('MAX_STRUCTURED_SYMBOLS', 16);
+
+ // -----------------------------------------------------
+
+ // Masks
+
+ /**
+ * Down point base value for case 1 mask pattern (concatenation of same color in a line or a column)
+ */
+ define('N1', 3);
+
+ /**
+ * Down point base value for case 2 mask pattern (module block of same color)
+ */
+ define('N2', 3);
+
+ /**
+ * Down point base value for case 3 mask pattern (1:1:3:1:1(dark:bright:dark:bright:dark)pattern in a line or a column)
+ */
+ define('N3', 40);
+
+ /**
+ * Down point base value for case 4 mask pattern (ration of dark modules in whole)
+ */
+ define('N4', 10);
+
+ // -----------------------------------------------------
+
+ // Optimization settings
+
+ /**
+ * if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code
+ */
+ define('QR_FIND_BEST_MASK', true);
+
+ /**
+ * if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly
+ */
+ define('QR_FIND_FROM_RANDOM', 2);
+
+ /**
+ * when QR_FIND_BEST_MASK === false
+ */
+ define('QR_DEFAULT_MASK', 2);
+
+ // -----------------------------------------------------
+
+} // end of definitions
+
+// #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#
+
+if (!class_exists('QRcode', false)) {
+
+ // for compaibility with PHP4
+ if (!function_exists('str_split')) {
+ /**
+ * Convert a string to an array (needed for PHP4 compatibility)
+ * @param string $string The input string.
+ * @param int $split_length Maximum length of the chunk.
+ * @return If the optional split_length parameter is specified, the returned array will be broken down into chunks with each being split_length in length, otherwise each chunk will be one character in length. FALSE is returned if split_length is less than 1. If the split_length length exceeds the length of string , the entire string is returned as the first (and only) array element.
+ */
+ function str_split($string, $split_length=1) {
+ if ((strlen($string) > $split_length) OR (!$split_length)) {
+ do {
+ $c = strlen($string);
+ $parts[] = substr($string, 0, $split_length);
+ $string = substr($string, $split_length);
+ } while ($string !== false);
+ } else {
+ $parts = array($string);
+ }
+ return $parts;
+ }
+ }
+
+ // #####################################################
+
+ /**
+ * Class to create QR-code arrays for TCPDF class.
+ * QR Code symbol is a 2D barcode that can be scanned by handy terminals such as a mobile phone with CCD.
+ * The capacity of QR Code is up to 7000 digits or 4000 characters, and has high robustness.
+ * This class supports QR Code model 2, described in JIS (Japanese Industrial Standards) X0510:2004 or ISO/IEC 18004.
+ * Currently the following features are not supported: ECI and FNC1 mode, Micro QR Code, QR Code model 1, Structured mode.
+ *
+ * This class is derived from "PHP QR Code encoder" by Dominik Dzienia (http://phpqrcode.sourceforge.net/) based on "libqrencode C library 3.1.1." by Kentaro Fukuchi (http://megaui.net/fukuchi/works/qrencode/index.en.html), contains Reed-Solomon code written by Phil Karn, KA9Q. QR Code is registered trademark of DENSO WAVE INCORPORATED (http://www.denso-wave.com/qrcode/index-e.html).
+ * Please read comments on this class source file for full copyright and license information.
+ *
+ * @name QRcode
+ * @package com.tecnick.tcpdf
+ * @abstract Class for generating QR-code array for TCPDF.
+ * @author Nicola Asuni
+ * @copyright 2010 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
+ * @link http://www.tcpdf.org
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ * @version 1.0.002
+ */
+ class QRcode {
+
+ /**
+ * @var barcode array to be returned which is readable by TCPDF
+ * @access protected
+ */
+ protected $barcode_array = array();
+
+ /**
+ * @var QR code version. Size of QRcode is defined as version. Version is from 1 to 40. Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases. So version 40 is 177*177 matrix.
+ * @access protected
+ */
+ protected $version = 0;
+
+ /**
+ * @var Levels of error correction. See definitions for possible values.
+ * @access protected
+ */
+ protected $level = QR_ECLEVEL_L;
+
+ /**
+ * @var Encoding mode
+ * @access protected
+ */
+ protected $hint = QR_MODE_8B;
+
+ /**
+ * @var if true the input string will be converted to uppercase
+ * @access protected
+ */
+ protected $casesensitive = true;
+
+ /**
+ * @var structured QR code (not supported yet)
+ * @access protected
+ */
+ protected $structured = 0;
+
+ /**
+ * @var mask data
+ * @access protected
+ */
+ protected $data;
+
+ // FrameFiller
+
+ /**
+ * @var width
+ * @access protected
+ */
+ protected $width;
+
+ /**
+ * @var frame
+ * @access protected
+ */
+ protected $frame;
+
+ /**
+ * @var X position of bit
+ * @access protected
+ */
+ protected $x;
+
+ /**
+ * @var Y position of bit
+ * @access protected
+ */
+ protected $y;
+
+ /**
+ * @var direction
+ * @access protected
+ */
+ protected $dir;
+
+ /**
+ * @var single bit
+ * @access protected
+ */
+ protected $bit;
+
+ // ---- QRrawcode ----
+
+ /**
+ * @var data code
+ * @access protected
+ */
+ protected $datacode = array();
+
+ /**
+ * @var error correction code
+ * @access protected
+ */
+ protected $ecccode = array();
+
+ /**
+ * @var blocks
+ * @access protected
+ */
+ protected $blocks;
+
+ /**
+ * @var Reed-Solomon blocks
+ * @access protected
+ */
+ protected $rsblocks = array(); //of RSblock
+
+ /**
+ * @var counter
+ * @access protected
+ */
+ protected $count;
+
+ /**
+ * @var data length
+ * @access protected
+ */
+ protected $dataLength;
+
+ /**
+ * @var error correction length
+ * @access protected
+ */
+ protected $eccLength;
+
+ /**
+ * @var b1
+ * @access protected
+ */
+ protected $b1;
+
+ // ---- QRmask ----
+
+ /**
+ * @var run length
+ * @access protected
+ */
+ protected $runLength = array();
+
+ // ---- QRsplit ----
+
+ /**
+ * @var input data string
+ * @access protected
+ */
+ protected $dataStr = '';
+
+ /**
+ * @var input items
+ * @access protected
+ */
+ protected $items;
+
+ // Reed-Solomon items
+
+ /**
+ * @var Reed-Solomon items
+ * @access protected
+ */
+ protected $rsitems = array();
+
+ /**
+ * @var array of frames
+ * @access protected
+ */
+ protected $frames = array();
+
+ /**
+ * @var alphabet-numeric convesion table
+ * @access protected
+ */
+ protected $anTable = array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, //
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, //
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, //
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, //
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 //
+ );
+
+ /**
+ * @var array Table of the capacity of symbols
+ * See Table 1 (pp.13) and Table 12-16 (pp.30-36), JIS X0510:2004.
+ * @access protected
+ */
+ protected $capacity = array(
+ array( 0, 0, 0, array( 0, 0, 0, 0)), //
+ array( 21, 26, 0, array( 7, 10, 13, 17)), // 1
+ array( 25, 44, 7, array( 10, 16, 22, 28)), //
+ array( 29, 70, 7, array( 15, 26, 36, 44)), //
+ array( 33, 100, 7, array( 20, 36, 52, 64)), //
+ array( 37, 134, 7, array( 26, 48, 72, 88)), // 5
+ array( 41, 172, 7, array( 36, 64, 96, 112)), //
+ array( 45, 196, 0, array( 40, 72, 108, 130)), //
+ array( 49, 242, 0, array( 48, 88, 132, 156)), //
+ array( 53, 292, 0, array( 60, 110, 160, 192)), //
+ array( 57, 346, 0, array( 72, 130, 192, 224)), // 10
+ array( 61, 404, 0, array( 80, 150, 224, 264)), //
+ array( 65, 466, 0, array( 96, 176, 260, 308)), //
+ array( 69, 532, 0, array( 104, 198, 288, 352)), //
+ array( 73, 581, 3, array( 120, 216, 320, 384)), //
+ array( 77, 655, 3, array( 132, 240, 360, 432)), // 15
+ array( 81, 733, 3, array( 144, 280, 408, 480)), //
+ array( 85, 815, 3, array( 168, 308, 448, 532)), //
+ array( 89, 901, 3, array( 180, 338, 504, 588)), //
+ array( 93, 991, 3, array( 196, 364, 546, 650)), //
+ array( 97, 1085, 3, array( 224, 416, 600, 700)), // 20
+ array(101, 1156, 4, array( 224, 442, 644, 750)), //
+ array(105, 1258, 4, array( 252, 476, 690, 816)), //
+ array(109, 1364, 4, array( 270, 504, 750, 900)), //
+ array(113, 1474, 4, array( 300, 560, 810, 960)), //
+ array(117, 1588, 4, array( 312, 588, 870, 1050)), // 25
+ array(121, 1706, 4, array( 336, 644, 952, 1110)), //
+ array(125, 1828, 4, array( 360, 700, 1020, 1200)), //
+ array(129, 1921, 3, array( 390, 728, 1050, 1260)), //
+ array(133, 2051, 3, array( 420, 784, 1140, 1350)), //
+ array(137, 2185, 3, array( 450, 812, 1200, 1440)), // 30
+ array(141, 2323, 3, array( 480, 868, 1290, 1530)), //
+ array(145, 2465, 3, array( 510, 924, 1350, 1620)), //
+ array(149, 2611, 3, array( 540, 980, 1440, 1710)), //
+ array(153, 2761, 3, array( 570, 1036, 1530, 1800)), //
+ array(157, 2876, 0, array( 570, 1064, 1590, 1890)), // 35
+ array(161, 3034, 0, array( 600, 1120, 1680, 1980)), //
+ array(165, 3196, 0, array( 630, 1204, 1770, 2100)), //
+ array(169, 3362, 0, array( 660, 1260, 1860, 2220)), //
+ array(173, 3532, 0, array( 720, 1316, 1950, 2310)), //
+ array(177, 3706, 0, array( 750, 1372, 2040, 2430)) // 40
+ );
+
+ /**
+ * @var array Length indicator
+ * @access protected
+ */
+ protected $lengthTableBits = array(
+ array(10, 12, 14),
+ array( 9, 11, 13),
+ array( 8, 16, 16),
+ array( 8, 10, 12)
+ );
+
+ /**
+ * @var array Table of the error correction code (Reed-Solomon block)
+ * See Table 12-16 (pp.30-36), JIS X0510:2004.
+ * @access protected
+ */
+ protected $eccTable = array(
+ array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)), //
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), //
+ array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)), //
+ array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)), //
+ array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5
+ array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)), //
+ array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)), //
+ array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)), //
+ array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)), //
+ array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), // 10
+ array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)), //
+ array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)), //
+ array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)), //
+ array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)), //
+ array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), // 15
+ array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)), //
+ array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)), //
+ array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)), //
+ array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)), //
+ array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), // 20
+ array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)), //
+ array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)), //
+ array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)), //
+ array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)), //
+ array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), // 25
+ array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)), //
+ array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)), //
+ array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)), //
+ array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)), //
+ array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), // 30
+ array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)), //
+ array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)), //
+ array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)), //
+ array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)), //
+ array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), // 35
+ array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)), //
+ array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)), //
+ array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)), //
+ array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)), //
+ array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)) // 40
+ );
+
+ /**
+ * @var array Positions of alignment patterns.
+ * This array includes only the second and the third position of the alignment patterns. Rest of them can be calculated from the distance between them.
+ * See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
+ * @access protected
+ */
+ protected $alignmentPattern = array(
+ array( 0, 0),
+ array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5
+ array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10
+ array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), // 11-15
+ array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), // 16-20
+ array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), // 21-25
+ array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), // 26-30
+ array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), // 31-35
+ array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58) // 35-40
+ );
+
+ /**
+ * @var array Version information pattern (BCH coded).
+ * See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
+ * size: [QRSPEC_VERSION_MAX - 6]
+ * @access protected
+ */
+ protected $versionPattern = array(
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, //
+ 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, //
+ 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, //
+ 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, //
+ 0x27541, 0x28c69
+ );
+
+ /**
+ * @var array Format information
+ * @access protected
+ */
+ protected $formatInfo = array(
+ array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976), //
+ array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0), //
+ array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed), //
+ array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b) //
+ );
+
+
+ // -------------------------------------------------
+ // -------------------------------------------------
+
+
+ /**
+ * This is the class constructor.
+ * Creates a QRcode object
+ * @param string $code code to represent using QRcode
+ * @param string $eclevel error level: L : About 7% or less errors can be corrected. M : About 15% or less errors can be corrected. Q : About 25% or less errors can be corrected. H : About 30% or less errors can be corrected.
+ * @access public
+ * @since 1.0.000
+ */
+ public function __construct($code, $eclevel = 'L') {
+ $barcode_array = array();
+ if ((is_null($code)) OR ($code == '\0') OR ($code == '')) {
+ return false;
+ }
+ // set error correction level
+ $this->level = array_search($eclevel, array('L', 'M', 'Q', 'H'));
+ if ($this->level === false) {
+ $this->level = QR_ECLEVEL_L;
+ }
+ if (($this->hint != QR_MODE_8B) AND ($this->hint != QR_MODE_KJ)) {
+ return false;
+ }
+ if (($this->version < 0) OR ($this->version > QRSPEC_VERSION_MAX)) {
+ return false;
+ }
+ $this->items = array();
+ $this->encodeString($code);
+ $qrTab = $this->binarize($this->data);
+ $size = count($qrTab);
+ $barcode_array['num_rows'] = $size;
+ $barcode_array['num_cols'] = $size;
+ $barcode_array['bcode'] = array();
+ foreach ($qrTab as $line) {
+ $arrAdd = array();
+ foreach (str_split($line) as $char) {
+ $arrAdd[] = ($char=='1')?1:0;
+ }
+ $barcode_array['bcode'][] = $arrAdd;
+ }
+ $this->barcode_array = $barcode_array;
+ }
+
+ /**
+ * Returns a barcode array which is readable by TCPDF
+ * @return array barcode array readable by TCPDF;
+ * @access public
+ */
+ public function getBarcodeArray() {
+ return $this->barcode_array;
+ }
+
+ /**
+ * Convert the frame in binary form
+ * @param array $frame array to binarize
+ * @return array frame in binary form
+ */
+ protected function binarize($frame) {
+ $len = count($frame);
+ // the frame is square (width = height)
+ foreach ($frame as &$frameLine) {
+ for ($i=0; $i<$len; $i++) {
+ $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
+ }
+ }
+ return $frame;
+ }
+
+ /**
+ * Encode the input string to QR code
+ * @param string $string input string to encode
+ */
+ protected function encodeString($string) {
+ $this->dataStr = $string;
+ if (!$this->casesensitive) {
+ $this->toUpper();
+ }
+ $ret = $this->splitString();
+ if ($ret < 0) {
+ return NULL;
+ }
+ $this->encodeMask(-1);
+ }
+
+ /**
+ * Encode mask
+ * @param int $mask masking mode
+ */
+ protected function encodeMask($mask) {
+ $spec = array(0, 0, 0, 0, 0);
+ $this->datacode = $this->getByteStream($this->items);
+ if (is_null($this->datacode)) {
+ return NULL;
+ }
+ $spec = $this->getEccSpec($this->version, $this->level, $spec);
+ $this->b1 = $this->rsBlockNum1($spec);
+ $this->dataLength = $this->rsDataLength($spec);
+ $this->eccLength = $this->rsEccLength($spec);
+ $this->ecccode = array_fill(0, $this->eccLength, 0);
+ $this->blocks = $this->rsBlockNum($spec);
+ $ret = $this->init($spec);
+ if ($ret < 0) {
+ return NULL;
+ }
+ $this->count = 0;
+ $this->width = $this->getWidth($this->version);
+ $this->frame = $this->newFrame($this->version);
+ $this->x = $this->width - 1;
+ $this->y = $this->width - 1;
+ $this->dir = -1;
+ $this->bit = -1;
+ // inteleaved data and ecc codes
+ for ($i=0; $i < ($this->dataLength + $this->eccLength); $i++) {
+ $code = $this->getCode();
+ $bit = 0x80;
+ for ($j=0; $j<8; $j++) {
+ $addr = $this->getNextPosition();
+ $this->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
+ $bit = $bit >> 1;
+ }
+ }
+ // remainder bits
+ $j = $this->getRemainder($this->version);
+ for ($i=0; $i<$j; $i++) {
+ $addr = $this->getNextPosition();
+ $this->setFrameAt($addr, 0x02);
+ }
+ // masking
+ $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
+ if ($mask < 0) {
+ if (QR_FIND_BEST_MASK) {
+ $masked = $this->mask($this->width, $this->frame, $this->level);
+ } else {
+ $masked = $this->makeMask($this->width, $this->frame, (intval(QR_DEFAULT_MASK) % 8), $this->level);
+ }
+ } else {
+ $masked = $this->makeMask($this->width, $this->frame, $mask, $this->level);
+ }
+ if ($masked == NULL) {
+ return NULL;
+ }
+ $this->data = $masked;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // FrameFiller
+
+ /**
+ * Set frame value at specified position
+ * @param array $at x,y position
+ * @param int $val value of the character to set
+ */
+ protected function setFrameAt($at, $val) {
+ $this->frame[$at['y']][$at['x']] = chr($val);
+ }
+
+ /**
+ * Get frame value at specified position
+ * @param array $at x,y position
+ * @return value at specified position
+ */
+ protected function getFrameAt($at) {
+ return ord($this->frame[$at['y']][$at['x']]);
+ }
+
+ /**
+ * Return the next frame position
+ * @return array of x,y coordinates
+ */
+ protected function getNextPosition() {
+ do {
+ if ($this->bit == -1) {
+ $this->bit = 0;
+ return array('x'=>$this->x, 'y'=>$this->y);
+ }
+ $x = $this->x;
+ $y = $this->y;
+ $w = $this->width;
+ if ($this->bit == 0) {
+ $x--;
+ $this->bit++;
+ } else {
+ $x++;
+ $y += $this->dir;
+ $this->bit--;
+ }
+ if ($this->dir < 0) {
+ if ($y < 0) {
+ $y = 0;
+ $x -= 2;
+ $this->dir = 1;
+ if ($x == 6) {
+ $x--;
+ $y = 9;
+ }
+ }
+ } else {
+ if ($y == $w) {
+ $y = $w - 1;
+ $x -= 2;
+ $this->dir = -1;
+ if ($x == 6) {
+ $x--;
+ $y -= 8;
+ }
+ }
+ }
+ if (($x < 0) OR ($y < 0)) {
+ return NULL;
+ }
+ $this->x = $x;
+ $this->y = $y;
+ } while(ord($this->frame[$y][$x]) & 0x80);
+ return array('x'=>$x, 'y'=>$y);
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRrawcode
+
+ /**
+ * Initialize code.
+ * @param array $spec array of ECC specification
+ * @return 0 in case of success, -1 in case of error
+ */
+ protected function init($spec) {
+ $dl = $this->rsDataCodes1($spec);
+ $el = $this->rsEccCodes1($spec);
+ $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+ $blockNo = 0;
+ $dataPos = 0;
+ $eccPos = 0;
+ $endfor = $this->rsBlockNum1($spec);
+ for ($i=0; $i < $endfor; ++$i) {
+ $ecc = array_slice($this->ecccode, $eccPos);
+ $this->rsblocks[$blockNo] = array();
+ $this->rsblocks[$blockNo]['dataLength'] = $dl;
+ $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos);
+ $this->rsblocks[$blockNo]['eccLength'] = $el;
+ $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
+ $this->rsblocks[$blockNo]['ecc'] = $ecc;
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+ if ($this->rsBlockNum2($spec) == 0) {
+ return 0;
+ }
+ $dl = $this->rsDataCodes2($spec);
+ $el = $this->rsEccCodes2($spec);
+ $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+ if ($rs == NULL) {
+ return -1;
+ }
+ $endfor = $this->rsBlockNum2($spec);
+ for ($i=0; $i < $endfor; ++$i) {
+ $ecc = array_slice($this->ecccode, $eccPos);
+ $this->rsblocks[$blockNo] = array();
+ $this->rsblocks[$blockNo]['dataLength'] = $dl;
+ $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos);
+ $this->rsblocks[$blockNo]['eccLength'] = $el;
+ $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
+ $this->rsblocks[$blockNo]['ecc'] = $ecc;
+ $this->ecccode = array_merge(array_slice($this->ecccode, 0, $eccPos), $ecc);
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+ return 0;
+ }
+
+ /**
+ * Return Reed-Solomon block code.
+ * @return array rsblocks
+ */
+ protected function getCode() {
+ if ($this->count < $this->dataLength) {
+ $row = $this->count % $this->blocks;
+ $col = $this->count / $this->blocks;
+ if ($col >= $this->rsblocks[0]['dataLength']) {
+ $row += $this->b1;
+ }
+ $ret = $this->rsblocks[$row]['data'][$col];
+ } elseif ($this->count < $this->dataLength + $this->eccLength) {
+ $row = ($this->count - $this->dataLength) % $this->blocks;
+ $col = ($this->count - $this->dataLength) / $this->blocks;
+ $ret = $this->rsblocks[$row]['ecc'][$col];
+ } else {
+ return 0;
+ }
+ $this->count++;
+ return $ret;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRmask
+
+ /**
+ * Write Format Information on frame and returns the number of black bits
+ * @param int $width frame width
+ * @param array $frame frame
+ * @param array $mask masking mode
+ * @param int $level error correction level
+ * @return int blacks
+ */
+ protected function writeFormatInformation($width, &$frame, $mask, $level) {
+ $blacks = 0;
+ $format = $this->getFormatInfo($mask, $level);
+ for ($i=0; $i<8; ++$i) {
+ if ($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+ $frame[8][$width - 1 - $i] = chr($v);
+ if ($i < 6) {
+ $frame[$i][8] = chr($v);
+ } else {
+ $frame[$i + 1][8] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+ for ($i=0; $i<7; ++$i) {
+ if ($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+ $frame[$width - 7 + $i][8] = chr($v);
+ if ($i == 0) {
+ $frame[8][7] = chr($v);
+ } else {
+ $frame[8][6 - $i] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+ return $blacks;
+ }
+
+ /**
+ * mask0
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask0($x, $y) {
+ return ($x + $y) & 1;
+ }
+
+ /**
+ * mask1
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask1($x, $y) {
+ return ($y & 1);
+ }
+
+ /**
+ * mask2
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask2($x, $y) {
+ return ($x % 3);
+ }
+
+ /**
+ * mask3
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask3($x, $y) {
+ return ($x + $y) % 3;
+ }
+
+ /**
+ * mask4
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask4($x, $y) {
+ return (((int)($y / 2)) + ((int)($x / 3))) & 1;
+ }
+
+ /**
+ * mask5
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask5($x, $y) {
+ return (($x * $y) & 1) + ($x * $y) % 3;
+ }
+
+ /**
+ * mask6
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask6($x, $y) {
+ return ((($x * $y) & 1) + ($x * $y) % 3) & 1;
+ }
+
+ /**
+ * mask7
+ * @param int $x X position
+ * @param int $y Y position
+ * @return int mask
+ */
+ protected function mask7($x, $y) {
+ return ((($x * $y) % 3) + (($x + $y) & 1)) & 1;
+ }
+
+ /**
+ * Return bitmask
+ * @param int $maskNo mask number
+ * @param int $width width
+ * @param array $frame frame
+ * @return array bitmask
+ */
+ protected function generateMaskNo($maskNo, $width, $frame) {
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+ for ($y=0; $y<$width; ++$y) {
+ for ($x=0; $x<$width; ++$x) {
+ if (ord($frame[$y][$x]) & 0x80) {
+ $bitMask[$y][$x] = 0;
+ } else {
+ $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
+ $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
+ }
+ }
+ }
+ return $bitMask;
+ }
+
+ /**
+ * makeMaskNo
+ * @param int $maskNo
+ * @param int $width
+ * @param int $s
+ * @param int $d
+ * @param boolean $maskGenOnly
+ * @return int b
+ */
+ protected function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly=false) {
+ $b = 0;
+ $bitMask = array();
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ if ($maskGenOnly) {
+ return;
+ }
+ $d = $s;
+ for ($y=0; $y<$width; ++$y) {
+ for ($x=0; $x<$width; ++$x) {
+ if ($bitMask[$y][$x] == 1) {
+ $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
+ }
+ $b += (int)(ord($d[$y][$x]) & 1);
+ }
+ }
+ return $b;
+ }
+
+ /**
+ * makeMask
+ * @param int $width
+ * @param array $frame
+ * @param int $maskNo
+ * @param int $level
+ * @return array mask
+ */
+ protected function makeMask($width, $frame, $maskNo, $level) {
+ $masked = array_fill(0, $width, str_repeat("\0", $width));
+ $this->makeMaskNo($maskNo, $width, $frame, $masked);
+ $this->writeFormatInformation($width, $masked, $maskNo, $level);
+ return $masked;
+ }
+
+ /**
+ * calcN1N3
+ * @param int $length
+ * @return int demerit
+ */
+ protected function calcN1N3($length) {
+ $demerit = 0;
+ for ($i=0; $i<$length; ++$i) {
+ if ($this->runLength[$i] >= 5) {
+ $demerit += (N1 + ($this->runLength[$i] - 5));
+ }
+ if ($i & 1) {
+ if (($i >= 3) AND ($i < ($length-2)) AND ($this->runLength[$i] % 3 == 0)) {
+ $fact = (int)($this->runLength[$i] / 3);
+ if (($this->runLength[$i-2] == $fact)
+ AND ($this->runLength[$i-1] == $fact)
+ AND ($this->runLength[$i+1] == $fact)
+ AND ($this->runLength[$i+2] == $fact)) {
+ if (($this->runLength[$i-3] < 0) OR ($this->runLength[$i-3] >= (4 * $fact))) {
+ $demerit += N3;
+ } elseif ((($i+3) >= $length) OR ($this->runLength[$i+3] >= (4 * $fact))) {
+ $demerit += N3;
+ }
+ }
+ }
+ }
+ }
+ return $demerit;
+ }
+
+ /**
+ * evaluateSymbol
+ * @param int $width
+ * @param array $frame
+ * @return int demerit
+ */
+ protected function evaluateSymbol($width, $frame) {
+ $head = 0;
+ $demerit = 0;
+ for ($y=0; $y<$width; ++$y) {
+ $head = 0;
+ $this->runLength[0] = 1;
+ $frameY = $frame[$y];
+ if ($y > 0) {
+ $frameYM = $frame[$y-1];
+ }
+ for ($x=0; $x<$width; ++$x) {
+ if (($x > 0) AND ($y > 0)) {
+ $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
+ $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
+ if (($b22 | ($w22 ^ 1)) & 1) {
+ $demerit += N2;
+ }
+ }
+ if (($x == 0) AND (ord($frameY[$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } elseif ($x > 0) {
+ if ((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+ $demerit += $this->calcN1N3($head+1);
+ }
+ for ($x=0; $x<$width; ++$x) {
+ $head = 0;
+ $this->runLength[0] = 1;
+ for ($y=0; $y<$width; ++$y) {
+ if (($y == 0) AND (ord($frame[$y][$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } elseif ($y > 0) {
+ if ((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+ $demerit += $this->calcN1N3($head+1);
+ }
+ return $demerit;
+ }
+
+ /**
+ * mask
+ * @param int $width
+ * @param array $frame
+ * @param int $level
+ * @return array best mask
+ */
+ protected function mask($width, $frame, $level) {
+ $minDemerit = PHP_INT_MAX;
+ $bestMaskNum = 0;
+ $bestMask = array();
+ $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7);
+ if (QR_FIND_FROM_RANDOM !== false) {
+ $howManuOut = 8 - (QR_FIND_FROM_RANDOM % 9);
+ for ($i = 0; $i < $howManuOut; ++$i) {
+ $remPos = rand (0, count($checked_masks)-1);
+ unset($checked_masks[$remPos]);
+ $checked_masks = array_values($checked_masks);
+ }
+ }
+ $bestMask = $frame;
+ foreach ($checked_masks as $i) {
+ $mask = array_fill(0, $width, str_repeat("\0", $width));
+ $demerit = 0;
+ $blacks = 0;
+ $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
+ $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
+ $blacks = (int)(100 * $blacks / ($width * $width));
+ $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
+ $demerit += $this->evaluateSymbol($width, $mask);
+ if ($demerit < $minDemerit) {
+ $minDemerit = $demerit;
+ $bestMask = $mask;
+ $bestMaskNum = $i;
+ }
+ }
+ return $bestMask;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRsplit
+
+ /**
+ * Return true if the character at specified position is a number
+ * @param string $str string
+ * @param int $pos characted position
+ * @return boolean true of false
+ */
+ protected function isdigitat($str, $pos) {
+ if ($pos >= strlen($str)) {
+ return false;
+ }
+ return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
+ }
+
+ /**
+ * Return true if the character at specified position is an alphanumeric character
+ * @param string $str string
+ * @param int $pos characted position
+ * @return boolean true of false
+ */
+ protected function isalnumat($str, $pos) {
+ if ($pos >= strlen($str)) {
+ return false;
+ }
+ return ($this->lookAnTable(ord($str[$pos])) >= 0);
+ }
+
+ /**
+ * identifyMode
+ * @param int $pos
+ * @return int mode
+ */
+ protected function identifyMode($pos) {
+ if ($pos >= strlen($this->dataStr)) {
+ return QR_MODE_NL;
+ }
+ $c = $this->dataStr[$pos];
+ if ($this->isdigitat($this->dataStr, $pos)) {
+ return QR_MODE_NM;
+ } elseif ($this->isalnumat($this->dataStr, $pos)) {
+ return QR_MODE_AN;
+ } elseif ($this->hint == QR_MODE_KJ) {
+ if ($pos+1 < strlen($this->dataStr)) {
+ $d = $this->dataStr[$pos+1];
+ $word = (ord($c) << 8) | ord($d);
+ if (($word >= 0x8140 && $word <= 0x9ffc) OR ($word >= 0xe040 && $word <= 0xebbf)) {
+ return QR_MODE_KJ;
+ }
+ }
+ }
+ return QR_MODE_8B;
+ }
+
+ /**
+ * eatNum
+ * @return int run
+ */
+ protected function eatNum() {
+ $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
+ $p = 0;
+ while($this->isdigitat($this->dataStr, $p)) {
+ $p++;
+ }
+ $run = $p;
+ $mode = $this->identifyMode($p);
+ if ($mode == QR_MODE_8B) {
+ $dif = $this->estimateBitsModeNum($run) + 4 + $ln
+ + $this->estimateBitsMode8(1) // + 4 + l8
+ - $this->estimateBitsMode8($run + 1); // - 4 - l8
+ if ($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ if ($mode == QR_MODE_AN) {
+ $dif = $this->estimateBitsModeNum($run) + 4 + $ln
+ + $this->estimateBitsModeAn(1) // + 4 + la
+ - $this->estimateBitsModeAn($run + 1);// - 4 - la
+ if ($dif > 0) {
+ return $this->eatAn();
+ }
+ }
+ $this->items = $this->appendNewInputItem($this->items, QR_MODE_NM, $run, str_split($this->dataStr));
+ return $run;
+ }
+
+ /**
+ * eatAn
+ * @return int run
+ */
+ protected function eatAn() {
+ $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
+ $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
+ $p = 0;
+ while($this->isalnumat($this->dataStr, $p)) {
+ if ($this->isdigitat($this->dataStr, $p)) {
+ $q = $p;
+ while($this->isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = $this->estimateBitsModeAn($p) // + 4 + la
+ + $this->estimateBitsModeNum($q - $p) + 4 + $ln
+ - $this->estimateBitsModeAn($q); // - 4 - la
+ if ($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+ $run = $p;
+ if (!$this->isalnumat($this->dataStr, $p)) {
+ $dif = $this->estimateBitsModeAn($run) + 4 + $la
+ + $this->estimateBitsMode8(1) // + 4 + l8
+ - $this->estimateBitsMode8($run + 1); // - 4 - l8
+ if ($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ $this->items = $this->appendNewInputItem($this->items, QR_MODE_AN, $run, str_split($this->dataStr));
+ return $run;
+ }
+
+ /**
+ * eatKanji
+ * @return int run
+ */
+ protected function eatKanji() {
+ $p = 0;
+ while($this->identifyMode($p) == QR_MODE_KJ) {
+ $p += 2;
+ }
+ $this->items = $this->appendNewInputItem($this->items, QR_MODE_KJ, $p, str_split($this->dataStr));
+ return $run;
+ }
+
+ /**
+ * eat8
+ * @return int run
+ */
+ protected function eat8() {
+ $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
+ $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
+ $p = 1;
+ $dataStrLen = strlen($this->dataStr);
+ while($p < $dataStrLen) {
+ $mode = $this->identifyMode($p);
+ if ($mode == QR_MODE_KJ) {
+ break;
+ }
+ if ($mode == QR_MODE_NM) {
+ $q = $p;
+ while($this->isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = $this->estimateBitsMode8($p) // + 4 + l8
+ + $this->estimateBitsModeNum($q - $p) + 4 + $ln
+ - $this->estimateBitsMode8($q); // - 4 - l8
+ if ($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } elseif ($mode == QR_MODE_AN) {
+ $q = $p;
+ while($this->isalnumat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = $this->estimateBitsMode8($p) // + 4 + l8
+ + $this->estimateBitsModeAn($q - $p) + 4 + $la
+ - $this->estimateBitsMode8($q); // - 4 - l8
+ if ($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+ $run = $p;
+ $this->items = $this->appendNewInputItem($this->items, QR_MODE_8B, $run, str_split($this->dataStr));
+ return $run;
+ }
+
+ /**
+ * splitString
+ */
+ protected function splitString() {
+ while (strlen($this->dataStr) > 0) {
+ if ($this->dataStr == '') {
+ return 0;
+ }
+ $mode = $this->identifyMode(0);
+ switch ($mode) {
+ case QR_MODE_NM: {
+ $length = $this->eatNum();
+ break;
+ }
+ case QR_MODE_AN: {
+ $length = $this->eatAn();
+ break;
+ }
+ case QR_MODE_KJ: {
+ if ($hint == QR_MODE_KJ) {
+ $length = $this->eatKanji();
+ } else {
+ $length = $this->eat8();
+ }
+ break;
+ }
+ default: {
+ $length = $this->eat8();
+ break;
+ }
+ }
+ if ($length == 0) {
+ return 0;
+ }
+ if ($length < 0) {
+ return -1;
+ }
+ $this->dataStr = substr($this->dataStr, $length);
+ }
+ }
+
+ /**
+ * toUpper
+ */
+ protected function toUpper() {
+ $stringLen = strlen($this->dataStr);
+ $p = 0;
+ while ($p < $stringLen) {
+ $mode = $this->identifyMode(substr($this->dataStr, $p), $this->hint);
+ if ($mode == QR_MODE_KJ) {
+ $p += 2;
+ } else {
+ if ((ord($this->dataStr[$p]) >= ord('a')) AND (ord($this->dataStr[$p]) <= ord('z'))) {
+ $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
+ }
+ $p++;
+ }
+ }
+ return $this->dataStr;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRinputItem
+
+ /**
+ * newInputItem
+ * @param int $mode
+ * @param int $size
+ * @param array $data
+ * @param array $bstream
+ * @return array input item
+ */
+ protected function newInputItem($mode, $size, $data, $bstream=null) {
+ $setData = array_slice($data, 0, $size);
+ if (count($setData) < $size) {
+ $setData = array_merge($setData, array_fill(0, ($size - count($setData)), 0));
+ }
+ if (!$this->check($mode, $size, $setData)) {
+ return NULL;
+ }
+ $inputitem = array();
+ $inputitem['mode'] = $mode;
+ $inputitem['size'] = $size;
+ $inputitem['data'] = $setData;
+ $inputitem['bstream'] = $bstream;
+ return $inputitem;
+ }
+
+ /**
+ * encodeModeNum
+ * @param array $inputitem
+ * @param int $version
+ * @return array input item
+ */
+ protected function encodeModeNum($inputitem, $version) {
+ $words = (int)($inputitem['size'] / 3);
+ $inputitem['bstream'] = array();
+ $val = 0x1;
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_NM, $version), $inputitem['size']);
+ for ($i=0; $i < $words; ++$i) {
+ $val = (ord($inputitem['data'][$i*3 ]) - ord('0')) * 100;
+ $val += (ord($inputitem['data'][$i*3+1]) - ord('0')) * 10;
+ $val += (ord($inputitem['data'][$i*3+2]) - ord('0'));
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val);
+ }
+ if ($inputitem['size'] - $words * 3 == 1) {
+ $val = ord($inputitem['data'][$words*3]) - ord('0');
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
+ } elseif (($inputitem['size'] - ($words * 3)) == 2) {
+ $val = (ord($inputitem['data'][$words*3 ]) - ord('0')) * 10;
+ $val += (ord($inputitem['data'][$words*3+1]) - ord('0'));
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val);
+ }
+ return $inputitem;
+ }
+
+ /**
+ * encodeModeAn
+ * @param array $inputitem
+ * @param int $version
+ * @return array input item
+ */
+ protected function encodeModeAn($inputitem, $version) {
+ $words = (int)($inputitem['size'] / 2);
+ $inputitem['bstream'] = array();
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x02);
+ $inputitem['bstream'] = $this->appendNum(v, $this->lengthIndicator(QR_MODE_AN, $version), $inputitem['size']);
+ for ($i=0; $i < $words; ++$i) {
+ $val = (int)$this->lookAnTable(ord($inputitem['data'][$i*2 ])) * 45;
+ $val += (int)$this->lookAnTable(ord($inputitem['data'][$i*2+1]));
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val);
+ }
+ if ($inputitem['size'] & 1) {
+ $val = $this->lookAnTable(ord($inputitem['data'][($words * 2)]));
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val);
+ }
+ return $inputitem;
+ }
+
+ /**
+ * encodeMode8
+ * @param array $inputitem
+ * @param int $version
+ * @return array input item
+ */
+ protected function encodeMode8($inputitem, $version) {
+ $inputitem['bstream'] = array();
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4);
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_8B, $version), $inputitem['size']);
+ for ($i=0; $i < $inputitem['size']; ++$i) {
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][$i]));
+ }
+ return $inputitem;
+ }
+
+ /**
+ * encodeModeKanji
+ * @param array $inputitem
+ * @param int $version
+ * @return array input item
+ */
+ protected function encodeModeKanji($inputitem, $version) {
+ $inputitem['bstream'] = array();
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8);
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_KJ, $version), (int)($inputitem['size'] / 2));
+ for ($i=0; $i<$inputitem['size']; $i+=2) {
+ $val = (ord($inputitem['data'][$i]) << 8) | ord($inputitem['data'][$i+1]);
+ if ($val <= 0x9ffc) {
+ $val -= 0x8140;
+ } else {
+ $val -= 0xc140;
+ }
+ $h = ($val >> 8) * 0xc0;
+ $val = ($val & 0xff) + $h;
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val);
+ }
+ return $inputitem;
+ }
+
+ /**
+ * encodeModeStructure
+ * @param array $inputitem
+ * @return array input item
+ */
+ protected function encodeModeStructure($inputitem) {
+ $inputitem['bstream'] = array();
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x03);
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][1]) - 1);
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][0]) - 1);
+ $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][2]));
+ return $inputitem;
+ }
+
+ /**
+ * encodeBitStream
+ * @param array $inputitem
+ * @param int $version
+ * @return array input item
+ */
+ protected function encodeBitStream($inputitem, $version) {
+ $inputitem['bstream'] = array();
+ $words = $this->maximumWords($inputitem['mode'], $version);
+ if ($inputitem['size'] > $words) {
+ $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']);
+ $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, array_slice($inputitem['data'], $words));
+ $st1 = $this->encodeBitStream($st1, $version);
+ $st2 = $this->encodeBitStream($st2, $version);
+ $inputitem['bstream'] = array();
+ $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']);
+ $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']);
+ } else {
+ switch($inputitem['mode']) {
+ case QR_MODE_NM: {
+ $inputitem = $this->encodeModeNum($inputitem, $version);
+ break;
+ }
+ case QR_MODE_AN: {
+ $inputitem = $this->encodeModeAn($inputitem, $version);
+ break;
+ }
+ case QR_MODE_8B: {
+ $inputitem = $this->encodeMode8($inputitem, $version);
+ break;
+ }
+ case QR_MODE_KJ: {
+ $inputitem = $this->encodeModeKanji($inputitem, $version);
+ break;
+ }
+ case QR_MODE_ST: {
+ $inputitem = $this->encodeModeStructure($inputitem);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ return $inputitem;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRinput
+
+ /**
+ * Append data to an input object.
+ * The data is copied and appended to the input object.
+ * @param array items input items
+ * @param int $mode encoding mode.
+ * @param int $size size of data (byte).
+ * @param array $data array of input data.
+ * @return items
+ *
+ */
+ protected function appendNewInputItem($items, $mode, $size, $data) {
+ $items[] = $this->newInputItem($mode, $size, $data);
+ return $items;
+ }
+
+ /**
+ * insertStructuredAppendHeader
+ * @param array $items
+ * @param int $size
+ * @param int $index
+ * @param int $parity
+ * @return array items
+ */
+ protected function insertStructuredAppendHeader($items, $size, $index, $parity) {
+ if ($size > MAX_STRUCTURED_SYMBOLS) {
+ return -1;
+ }
+ if (($index <= 0) OR ($index > MAX_STRUCTURED_SYMBOLS)) {
+ return -1;
+ }
+ $buf = array($size, $index, $parity);
+ $entry = $this->newInputItem(QR_MODE_ST, 3, buf);
+ array_unshift($items, $entry);
+ return $items;
+ }
+
+ /**
+ * calcParity
+ * @param array $items
+ * @return int parity
+ */
+ protected function calcParity($items) {
+ $parity = 0;
+ foreach ($items as $item) {
+ if ($item['mode'] != QR_MODE_ST) {
+ for ($i=$item['size']-1; $i>=0; --$i) {
+ $parity ^= $item['data'][$i];
+ }
+ }
+ }
+ return $parity;
+ }
+
+ /**
+ * checkModeNum
+ * @param int $size
+ * @param array $data
+ * @return boolean true or false
+ */
+ protected function checkModeNum($size, $data) {
+ for ($i=0; $i<$size; ++$i) {
+ if ((ord($data[$i]) < ord('0')) OR (ord($data[$i]) > ord('9'))){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * estimateBitsModeNum
+ * @param int $size
+ * @return int number of bits
+ */
+ protected function estimateBitsModeNum($size) {
+ $w = (int)$size / 3;
+ $bits = $w * 10;
+ switch($size - $w * 3) {
+ case 1: {
+ $bits += 4;
+ break;
+ }
+ case 2: {
+ $bits += 7;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ return $bits;
+ }
+
+ /**
+ * Look up the alphabet-numeric convesion table (see JIS X0510:2004, pp.19).
+ * @param int $c character value
+ * @return value
+ */
+ protected function lookAnTable($c) {
+ return (($c > 127)?-1:$this->anTable[$c]);
+ }
+
+ /**
+ * checkModeAn
+ * @param int $size
+ * @param array $data
+ * @return boolean true or false
+ */
+ protected function checkModeAn($size, $data) {
+ for ($i=0; $i<$size; ++$i) {
+ if ($this->lookAnTable(ord($data[$i])) == -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * estimateBitsModeAn
+ * @param int $size
+ * @return int number of bits
+ */
+ protected function estimateBitsModeAn($size) {
+ $w = (int)($size / 2);
+ $bits = $w * 11;
+ if ($size & 1) {
+ $bits += 6;
+ }
+ return $bits;
+ }
+
+ /**
+ * estimateBitsMode8
+ * @param int $size
+ * @return int number of bits
+ */
+ protected function estimateBitsMode8($size) {
+ return $size * 8;
+ }
+
+ /**
+ * estimateBitsModeKanji
+ * @param int $size
+ * @return int number of bits
+ */
+ protected function estimateBitsModeKanji($size) {
+ return (int)(($size / 2) * 13);
+ }
+
+ /**
+ * checkModeKanji
+ * @param int $size
+ * @param array $data
+ * @return boolean true or false
+ */
+ protected function checkModeKanji($size, $data) {
+ if ($size & 1) {
+ return false;
+ }
+ for ($i=0; $i<$size; $i+=2) {
+ $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
+ if (($val < 0x8140) OR (($val > 0x9ffc) AND ($val < 0xe040)) OR ($val > 0xebbf)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Validate the input data.
+ * @param int $mode encoding mode.
+ * @param int $size size of data (byte).
+ * @param array data data to validate
+ * @return boolean true in case of valid data, false otherwise
+ */
+ protected function check($mode, $size, $data) {
+ if ($size <= 0) {
+ return false;
+ }
+ switch($mode) {
+ case QR_MODE_NM: {
+ return $this->checkModeNum($size, $data);
+ }
+ case QR_MODE_AN: {
+ return $this->checkModeAn($size, $data);
+ }
+ case QR_MODE_KJ: {
+ return $this->checkModeKanji($size, $data);
+ }
+ case QR_MODE_8B: {
+ return true;
+ }
+ case QR_MODE_ST: {
+ return true;
+ }
+ default: {
+ break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * estimateBitStreamSize
+ * @param array $items
+ * @param int $version
+ * @return int bits
+ */
+ protected function estimateBitStreamSize($items, $version) {
+ $bits = 0;
+ if ($version == 0) {
+ $version = 1;
+ }
+ foreach ($items as $item) {
+ switch($item['mode']) {
+ case QR_MODE_NM: {
+ $bits = $this->estimateBitsModeNum($item['size']);
+ break;
+ }
+ case QR_MODE_AN: {
+ $bits = $this->estimateBitsModeAn($item['size']);
+ break;
+ }
+ case QR_MODE_8B: {
+ $bits = $this->estimateBitsMode8($item['size']);
+ break;
+ }
+ case QR_MODE_KJ: {
+ $bits = $this->estimateBitsModeKanji($item['size']);
+ break;
+ }
+ case QR_MODE_ST: {
+ return STRUCTURE_HEADER_BITS;
+ }
+ default: {
+ return 0;
+ }
+ }
+ $l = $this->lengthIndicator($item['mode'], $version);
+ $m = 1 << $l;
+ $num = (int)(($item['size'] + $m - 1) / $m);
+ $bits += $num * (4 + $l);
+ }
+ return $bits;
+ }
+
+ /**
+ * estimateVersion
+ * @param array $items
+ * @return int version
+ */
+ protected function estimateVersion($items) {
+ $version = 0;
+ $prev = 0;
+ do {
+ $prev = $version;
+ $bits = $this->estimateBitStreamSize($items, $prev);
+ $version = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($version < 0) {
+ return -1;
+ }
+ } while ($version > $prev);
+ return $version;
+ }
+
+ /**
+ * lengthOfCode
+ * @param int $mode
+ * @param int $version
+ * @param int $bits
+ * @return int size
+ */
+ protected function lengthOfCode($mode, $version, $bits) {
+ $payload = $bits - 4 - $this->lengthIndicator($mode, $version);
+ switch($mode) {
+ case QR_MODE_NM: {
+ $chunks = (int)($payload / 10);
+ $remain = $payload - $chunks * 10;
+ $size = $chunks * 3;
+ if ($remain >= 7) {
+ $size += 2;
+ } elseif ($remain >= 4) {
+ $size += 1;
+ }
+ break;
+ }
+ case QR_MODE_AN: {
+ $chunks = (int)($payload / 11);
+ $remain = $payload - $chunks * 11;
+ $size = $chunks * 2;
+ if ($remain >= 6) {
+ ++$size;
+ }
+ break;
+ }
+ case QR_MODE_8B: {
+ $size = (int)($payload / 8);
+ break;
+ }
+ case QR_MODE_KJ: {
+ $size = (int)(($payload / 13) * 2);
+ break;
+ }
+ case QR_MODE_ST: {
+ $size = (int)($payload / 8);
+ break;
+ }
+ default: {
+ $size = 0;
+ break;
+ }
+ }
+ $maxsize = $this->maximumWords($mode, $version);
+ if ($size < 0) {
+ $size = 0;
+ }
+ if ($size > $maxsize) {
+ $size = $maxsize;
+ }
+ return $size;
+ }
+
+ /**
+ * createBitStream
+ * @param array $items
+ * @return array of items and total bits
+ */
+ protected function createBitStream($items) {
+ $total = 0;
+ foreach ($items as $key => $item) {
+ $items[$key] = $this->encodeBitStream($item, $this->version);
+ $bits = count($items[$key]['bstream']);
+ $total += $bits;
+ }
+ return array($items, $total);
+ }
+
+ /**
+ * convertData
+ * @param array $items
+ * @return array items
+ */
+ protected function convertData($items) {
+ $ver = $this->estimateVersion($items);
+ if ($ver > $this->version) {
+ $this->version = $ver;
+ }
+ for (;;) {
+ $cbs = $this->createBitStream($items);
+ $items = $cbs[0];
+ $bits = $cbs[1];
+ if ($bits < 0) {
+ return -1;
+ }
+ $ver = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($ver < 0) {
+ return -1;
+ } elseif ($ver > $this->version) {
+ $this->version = $ver;
+ } else {
+ break;
+ }
+ }
+ return $items;
+ }
+
+ /**
+ * Append Padding Bit to bitstream
+ * @param array $bstream
+ * @return array bitstream
+ */
+ protected function appendPaddingBit($bstream) {
+ $bits = count($bstream);
+ $maxwords = $this->getDataLength($this->version, $this->level);
+ $maxbits = $maxwords * 8;
+ if ($maxbits == $bits) {
+ return 0;
+ }
+ if ($maxbits - $bits < 5) {
+ return $this->appendNum($bstream, $maxbits - $bits, 0);
+ }
+ $bits += 4;
+ $words = (int)(($bits + 7) / 8);
+ $padding = array();
+ $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0);
+ $padlen = $maxwords - $words;
+ if ($padlen > 0) {
+ $padbuf = array();
+ for ($i=0; $i<$padlen; ++$i) {
+ $padbuf[$i] = ($i&1)?0x11:0xec;
+ }
+ $padding = $this->appendBytes($padding, $padlen, $padbuf);
+ }
+ return $this->appendBitstream($bstream, $padding);
+ }
+
+ /**
+ * mergeBitStream
+ * @param array $bstream
+ * @return array bitstream
+ */
+ protected function mergeBitStream($items) {
+ $items = $this->convertData($items);
+ $bstream = array();
+ foreach ($items as $item) {
+ $bstream = $this->appendBitstream($bstream, $item['bstream']);
+ }
+ return $bstream;
+ }
+
+ /**
+ * Returns a stream of bits.
+ * @param int $items
+ * @return array padded merged byte stream
+ */
+ protected function getBitStream($items) {
+ $bstream = $this->mergeBitStream($items);
+ return $this->appendPaddingBit($bstream);
+ }
+
+ /**
+ * Pack all bit streams padding bits into a byte array.
+ * @param int $items
+ * @return array padded merged byte stream
+ */
+ protected function getByteStream($items) {
+ $bstream = $this->getBitStream($items);
+ return $this->bitstreamToByte($bstream);
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRbitstream
+
+ /**
+ * Return an array with zeros
+ * @param int $setLength array size
+ * @return array
+ */
+ protected function allocate($setLength) {
+ return array_fill(0, $setLength, 0);
+ }
+
+ /**
+ * Return new bitstream from number
+ * @param int $bits number of bits
+ * @param int $num number
+ * @return array bitstream
+ */
+ protected function newFromNum($bits, $num) {
+ $bstream = $this->allocate($bits);
+ $mask = 1 << ($bits - 1);
+ for ($i=0; $i<$bits; ++$i) {
+ if ($num & $mask) {
+ $bstream[$i] = 1;
+ } else {
+ $bstream[$i] = 0;
+ }
+ $mask = $mask >> 1;
+ }
+ return $bstream;
+ }
+
+ /**
+ * Return new bitstream from bytes
+ * @param int $size size
+ * @param array $data bytes
+ * @return array bitstream
+ */
+ protected function newFromBytes($size, $data) {
+ $bstream = $this->allocate($size * 8);
+ $p=0;
+ for ($i=0; $i<$size; ++$i) {
+ $mask = 0x80;
+ for ($j=0; $j<8; ++$j) {
+ if ($data[$i] & $mask) {
+ $bstream[$p] = 1;
+ } else {
+ $bstream[$p] = 0;
+ }
+ $p++;
+ $mask = $mask >> 1;
+ }
+ }
+ return $bstream;
+ }
+
+ /**
+ * Append one bitstream to another
+ * @param array $bitstream original bitstream
+ * @param array $append bitstream to append
+ * @return array bitstream
+ */
+ protected function appendBitstream($bitstream, $append) {
+ if ((!is_array($append)) OR (count($append) == 0)) {
+ return $bitstream;
+ }
+ if (count($bitstream) == 0) {
+ return $append;
+ }
+ return array_values(array_merge($bitstream, $append));
+ }
+
+ /**
+ * Append one bitstream created from number to another
+ * @param array $bitstream original bitstream
+ * @param int $bits number of bits
+ * @param int $num number
+ * @return array bitstream
+ */
+ protected function appendNum($bitstream, $bits, $num) {
+ if ($bits == 0) {
+ return 0;
+ }
+ $b = $this->newFromNum($bits, $num);
+ return $this->appendBitstream($bitstream, $b);
+ }
+
+ /**
+ * Append one bitstream created from bytes to another
+ * @param array $bitstream original bitstream
+ * @param int $size size
+ * @param array $data bytes
+ * @return array bitstream
+ */
+ protected function appendBytes($bitstream, $size, $data) {
+ if ($size == 0) {
+ return 0;
+ }
+ $b = $this->newFromBytes($size, $data);
+ return $this->appendBitstream($bitstream, $b);
+ }
+
+ /**
+ * Convert bitstream to bytes
+ * @param array $bitstream original bitstream
+ * @return array of bytes
+ */
+ protected function bitstreamToByte($bstream) {
+ $size = count($bstream);
+ if ($size == 0) {
+ return array();
+ }
+ $data = array_fill(0, (int)(($size + 7) / 8), 0);
+ $bytes = (int)($size / 8);
+ $p = 0;
+ for ($i=0; $i<$bytes; $i++) {
+ $v = 0;
+ for ($j=0; $j<8; $j++) {
+ $v = $v << 1;
+ $v |= $bstream[$p];
+ $p++;
+ }
+ $data[$i] = $v;
+ }
+ if ($size & 7) {
+ $v = 0;
+ for ($j=0; $j<($size & 7); $j++) {
+ $v = $v << 1;
+ $v |= $bstream[$p];
+ $p++;
+ }
+ $data[$bytes] = $v;
+ }
+ return $data;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRspec
+
+ /**
+ * Replace a value on the array at the specified position
+ * @param array $srctab
+ * @param int $x X position
+ * @param int $y Y position
+ * @param string $repl value to replace
+ * @param int $replLen length of the repl string
+ * @return array srctab
+ */
+ protected function qrstrset($srctab, $x, $y, $repl, $replLen=false) {
+ $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
+ return $srctab;
+ }
+
+ /**
+ * Return maximum data code length (bytes) for the version.
+ * @param int $version version
+ * @param int $level error correction level
+ * @return int maximum size (bytes)
+ */
+ protected function getDataLength($version, $level) {
+ return $this->capacity[$version][QRCAP_WORDS] - $this->capacity[$version][QRCAP_EC][$level];
+ }
+
+ /**
+ * Return maximum error correction code length (bytes) for the version.
+ * @param int $version version
+ * @param int $level error correction level
+ * @return int ECC size (bytes)
+ */
+ protected function getECCLength($version, $level){
+ return $this->capacity[$version][QRCAP_EC][$level];
+ }
+
+ /**
+ * Return the width of the symbol for the version.
+ * @param int $version version
+ * @return int width
+ */
+ protected function getWidth($version) {
+ return $this->capacity[$version][QRCAP_WIDTH];
+ }
+
+ /**
+ * Return the numer of remainder bits.
+ * @param int $version version
+ * @return int number of remainder bits
+ */
+ protected function getRemainder($version) {
+ return $this->capacity[$version][QRCAP_REMINDER];
+ }
+
+ /**
+ * Return a version number that satisfies the input code length.
+ * @param int $size input code length (byte)
+ * @param int $level error correction level
+ * @return int version number
+ */
+ protected function getMinimumVersion($size, $level) {
+ for ($i=1; $i <= QRSPEC_VERSION_MAX; ++$i) {
+ $words = $this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level];
+ if ($words >= $size) {
+ return $i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the size of length indicator for the mode and version.
+ * @param int $mode encoding mode
+ * @param int $version version
+ * @return int the size of the appropriate length indicator (bits).
+ */
+ protected function lengthIndicator($mode, $version) {
+ if ($mode == QR_MODE_ST) {
+ return 0;
+ }
+ if ($version <= 9) {
+ $l = 0;
+ } elseif ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+ return $this->lengthTableBits[$mode][$l];
+ }
+
+ /**
+ * Return the maximum length for the mode and version.
+ * @param int $mode encoding mode
+ * @param int $version version
+ * @return int the maximum length (bytes)
+ */
+ protected function maximumWords($mode, $version) {
+ if ($mode == QR_MODE_ST) {
+ return 3;
+ }
+ if ($version <= 9) {
+ $l = 0;
+ } else if ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+ $bits = $this->lengthTableBits[$mode][$l];
+ $words = (1 << $bits) - 1;
+ if ($mode == QR_MODE_KJ) {
+ $words *= 2; // the number of bytes is required
+ }
+ return $words;
+ }
+
+ /**
+ * Return an array of ECC specification.
+ * @param int $version version
+ * @param int $level error correction level
+ * @param array $spec an array of ECC specification contains as following: {# of type1 blocks, # of data code, # of ecc code, # of type2 blocks, # of data code}
+ * @return array spec
+ */
+ protected function getEccSpec($version, $level, $spec) {
+ if (count($spec) < 5) {
+ $spec = array(0, 0, 0, 0, 0);
+ }
+ $b1 = $this->eccTable[$version][$level][0];
+ $b2 = $this->eccTable[$version][$level][1];
+ $data = $this->getDataLength($version, $level);
+ $ecc = $this->getECCLength($version, $level);
+ if ($b2 == 0) {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / $b1);
+ $spec[2] = (int)($ecc / $b1);
+ $spec[3] = 0;
+ $spec[4] = 0;
+ } else {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / ($b1 + $b2));
+ $spec[2] = (int)($ecc / ($b1 + $b2));
+ $spec[3] = $b2;
+ $spec[4] = $spec[1] + 1;
+ }
+ return $spec;
+ }
+
+ /**
+ * Put an alignment marker.
+ * @param array $frame frame
+ * @param int $width width
+ * @param int $ox X center coordinate of the pattern
+ * @param int $oy Y center coordinate of the pattern
+ * @return array frame
+ */
+ protected function putAlignmentMarker($frame, $ox, $oy) {
+ $finder = array(
+ "\xa1\xa1\xa1\xa1\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa0\xa1\xa0\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa1\xa1\xa1\xa1"
+ );
+ $yStart = $oy - 2;
+ $xStart = $ox - 2;
+ for ($y=0; $y < 5; $y++) {
+ $frame = $this->qrstrset($frame, $xStart, $yStart+$y, $finder[$y]);
+ }
+ return $frame;
+ }
+
+ /**
+ * Put an alignment pattern.
+ * @param int $version version
+ * @param array $fram frame
+ * @param int $width width
+ * @return array frame
+ */
+ protected function putAlignmentPattern($version, $frame, $width) {
+ if ($version < 2) {
+ return $frame;
+ }
+ $d = $this->alignmentPattern[$version][1] - $this->alignmentPattern[$version][0];
+ if ($d < 0) {
+ $w = 2;
+ } else {
+ $w = (int)(($width - $this->alignmentPattern[$version][0]) / $d + 2);
+ }
+ if ($w * $w - 3 == 1) {
+ $x = $this->alignmentPattern[$version][0];
+ $y = $this->alignmentPattern[$version][0];
+ $frame = $this->putAlignmentMarker($frame, $x, $y);
+ return $frame;
+ }
+ $cx = $this->alignmentPattern[$version][0];
+ $wo = $w - 1;
+ for ($x=1; $x < $wo; ++$x) {
+ $frame = $this->putAlignmentMarker($frame, 6, $cx);
+ $frame = $this->putAlignmentMarker($frame, $cx, 6);
+ $cx += $d;
+ }
+ $cy = $this->alignmentPattern[$version][0];
+ for ($y=0; $y < $wo; ++$y) {
+ $cx = $this->alignmentPattern[$version][0];
+ for ($x=0; $x < $wo; ++$x) {
+ $frame = $this->putAlignmentMarker($frame, $cx, $cy);
+ $cx += $d;
+ }
+ $cy += $d;
+ }
+ return $frame;
+ }
+
+ /**
+ * Return BCH encoded version information pattern that is used for the symbol of version 7 or greater. Use lower 18 bits.
+ * @param int $version version
+ * @return BCH encoded version information pattern
+ */
+ protected function getVersionPattern($version) {
+ if (($version < 7) OR ($version > QRSPEC_VERSION_MAX)) {
+ return 0;
+ }
+ return $this->versionPattern[($version - 7)];
+ }
+
+ /**
+ * Return BCH encoded format information pattern.
+ * @param array $mask
+ * @param int $level error correction level
+ * @return BCH encoded format information pattern
+ */
+ protected function getFormatInfo($mask, $level) {
+ if (($mask < 0) OR ($mask > 7)) {
+ return 0;
+ }
+ if (($level < 0) OR ($level > 3)) {
+ return 0;
+ }
+ return $this->formatInfo[$level][$mask];
+ }
+
+ /**
+ * Put a finder pattern.
+ * @param array $frame frame
+ * @param int $width width
+ * @param int $ox X center coordinate of the pattern
+ * @param int $oy Y center coordinate of the pattern
+ * @return array frame
+ */
+ protected function putFinderPattern($frame, $ox, $oy) {
+ $finder = array(
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
+ );
+ for ($y=0; $y < 7; $y++) {
+ $frame = $this->qrstrset($frame, $ox, ($oy + $y), $finder[$y]);
+ }
+ return $frame;
+ }
+
+ /**
+ * Return a copy of initialized frame.
+ * @param int $version version
+ * @return Array of unsigned char.
+ */
+ protected function createFrame($version) {
+ $width = $this->capacity[$version][QRCAP_WIDTH];
+ $frameLine = str_repeat ("\0", $width);
+ $frame = array_fill(0, $width, $frameLine);
+ // Finder pattern
+ $frame = $this->putFinderPattern($frame, 0, 0);
+ $frame = $this->putFinderPattern($frame, $width - 7, 0);
+ $frame = $this->putFinderPattern($frame, 0, $width - 7);
+ // Separator
+ $yOffset = $width - 7;
+ for ($y=0; $y < 7; ++$y) {
+ $frame[$y][7] = "\xc0";
+ $frame[$y][$width - 8] = "\xc0";
+ $frame[$yOffset][7] = "\xc0";
+ ++$yOffset;
+ }
+ $setPattern = str_repeat("\xc0", 8);
+ $frame = $this->qrstrset($frame, 0, 7, $setPattern);
+ $frame = $this->qrstrset($frame, $width-8, 7, $setPattern);
+ $frame = $this->qrstrset($frame, 0, $width - 8, $setPattern);
+ // Format info
+ $setPattern = str_repeat("\x84", 9);
+ $frame = $this->qrstrset($frame, 0, 8, $setPattern);
+ $frame = $this->qrstrset($frame, $width - 8, 8, $setPattern, 8);
+ $yOffset = $width - 8;
+ for ($y=0; $y < 8; ++$y,++$yOffset) {
+ $frame[$y][8] = "\x84";
+ $frame[$yOffset][8] = "\x84";
+ }
+ // Timing pattern
+ $wo = $width - 15;
+ for ($i=1; $i < $wo; ++$i) {
+ $frame[6][7+$i] = chr(0x90 | ($i & 1));
+ $frame[7+$i][6] = chr(0x90 | ($i & 1));
+ }
+ // Alignment pattern
+ $frame = $this->putAlignmentPattern($version, $frame, $width);
+ // Version information
+ if ($version >= 7) {
+ $vinf = $this->getVersionPattern($version);
+ $v = $vinf;
+ for ($x=0; $x<6; ++$x) {
+ for ($y=0; $y<3; ++$y) {
+ $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+ $v = $vinf;
+ for ($y=0; $y<6; ++$y) {
+ for ($x=0; $x<3; ++$x) {
+ $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+ }
+ // and a little bit...
+ $frame[$width - 8][8] = "\x81";
+ return $frame;
+ }
+
+ /**
+ * Set new frame for the specified version.
+ * @param int $version version
+ * @return Array of unsigned char.
+ */
+ protected function newFrame($version) {
+ if (($version < 1) OR ($version > QRSPEC_VERSION_MAX)) {
+ return NULL;
+ }
+ if (!isset($this->frames[$version])) {
+ $this->frames[$version] = $this->createFrame($version);
+ }
+ if (is_null($this->frames[$version])) {
+ return NULL;
+ }
+ return $this->frames[$version];
+ }
+
+ /**
+ * Return block number 0
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsBlockNum($spec) {
+ return ($spec[0] + $spec[3]);
+ }
+
+ /**
+ * Return block number 1
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsBlockNum1($spec) {
+ return $spec[0];
+ }
+
+ /**
+ * Return data codes 1
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsDataCodes1($spec) {
+ return $spec[1];
+ }
+
+ /**
+ * Return ecc codes 1
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsEccCodes1($spec) {
+ return $spec[2];
+ }
+
+ /**
+ * Return block number 2
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsBlockNum2($spec) {
+ return $spec[3];
+ }
+
+ /**
+ * Return data codes 2
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsDataCodes2($spec) {
+ return $spec[4];
+ }
+
+ /**
+ * Return ecc codes 2
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsEccCodes2($spec) {
+ return $spec[2];
+ }
+
+ /**
+ * Return data length
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsDataLength($spec) {
+ return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]);
+ }
+
+ /**
+ * Return ecc length
+ * @param array $spec
+ * @return int value
+ */
+ protected function rsEccLength($spec) {
+ return ($spec[0] + $spec[3]) * $spec[2];
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRrs
+
+ /**
+ * Initialize a Reed-Solomon codec and add it to existing rsitems
+ * @param int $symsize symbol size, bits
+ * @param int $gfpoly Field generator polynomial coefficients
+ * @param int $fcr first root of RS code generator polynomial, index form
+ * @param int $prim primitive element to generate polynomial roots
+ * @param int $nroots RS code generator polynomial degree (number of roots)
+ * @param int $pad padding bytes at front of shortened block
+ * @return array Array of RS values:mm = Bits per symbol; nn = Symbols per block; alpha_to = log lookup table array; index_of = Antilog lookup table array; genpoly = Generator polynomial array; nroots = Number of generator; roots = number of parity symbols; fcr = First consecutive root, index form; prim = Primitive element, index form; iprim = prim-th root of 1, index form; pad = Padding bytes in shortened block; gfpoly .
+ */
+ protected function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
+ foreach ($this->rsitems as $rs) {
+ if (($rs['pad'] != $pad) OR ($rs['nroots'] != $nroots) OR ($rs['mm'] != $symsize)
+ OR ($rs['gfpoly'] != $gfpoly) OR ($rs['fcr'] != $fcr) OR ($rs['prim'] != $prim)) {
+ continue;
+ }
+ return $rs;
+ }
+ $rs = $this->init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
+ array_unshift($this->rsitems, $rs);
+ return $rs;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // QRrsItem
+
+ /**
+ * modnn
+ * @param array RS values
+ * @param int $x X position
+ * @return int X osition
+ */
+ protected function modnn($rs, $x) {
+ while ($x >= $rs['nn']) {
+ $x -= $rs['nn'];
+ $x = ($x >> $rs['mm']) + ($x & $rs['nn']);
+ }
+ return $x;
+ }
+
+ /**
+ * Initialize a Reed-Solomon codec and returns an array of values.
+ * @param int $symsize symbol size, bits
+ * @param int $gfpoly Field generator polynomial coefficients
+ * @param int $fcr first root of RS code generator polynomial, index form
+ * @param int $prim primitive element to generate polynomial roots
+ * @param int $nroots RS code generator polynomial degree (number of roots)
+ * @param int $pad padding bytes at front of shortened block
+ * @return array Array of RS values:mm = Bits per symbol; nn = Symbols per block; alpha_to = log lookup table array; index_of = Antilog lookup table array; genpoly = Generator polynomial array; nroots = Number of generator; roots = number of parity symbols; fcr = First consecutive root, index form; prim = Primitive element, index form; iprim = prim-th root of 1, index form; pad = Padding bytes in shortened block; gfpoly .
+ */
+ protected function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
+ // Based on Reed solomon encoder by Phil Karn, KA9Q (GNU-LGPLv2)
+ $rs = null;
+ // Check parameter ranges
+ if (($symsize < 0) OR ($symsize > 8)) {
+ return $rs;
+ }
+ if (($fcr < 0) OR ($fcr >= (1<<$symsize))) {
+ return $rs;
+ }
+ if (($prim <= 0) OR ($prim >= (1<<$symsize))) {
+ return $rs;
+ }
+ if (($nroots < 0) OR ($nroots >= (1<<$symsize))) {
+ return $rs;
+ }
+ if (($pad < 0) OR ($pad >= ((1<<$symsize) -1 - $nroots))) {
+ return $rs;
+ }
+ $rs = array();
+ $rs['mm'] = $symsize;
+ $rs['nn'] = (1 << $symsize) - 1;
+ $rs['pad'] = $pad;
+ $rs['alpha_to'] = array_fill(0, ($rs['nn'] + 1), 0);
+ $rs['index_of'] = array_fill(0, ($rs['nn'] + 1), 0);
+ // PHP style macro replacement ;)
+ $NN =& $rs['nn'];
+ $A0 =& $NN;
+ // Generate Galois field lookup tables
+ $rs['index_of'][0] = $A0; // log(zero) = -inf
+ $rs['alpha_to'][$A0] = 0; // alpha**-inf = 0
+ $sr = 1;
+ for ($i=0; $i<$rs['nn']; ++$i) {
+ $rs['index_of'][$sr] = $i;
+ $rs['alpha_to'][$i] = $sr;
+ $sr <<= 1;
+ if ($sr & (1 << $symsize)) {
+ $sr ^= $gfpoly;
+ }
+ $sr &= $rs['nn'];
+ }
+ if ($sr != 1) {
+ // field generator polynomial is not primitive!
+ return NULL;
+ }
+ // Form RS code generator polynomial from its roots
+ $rs['genpoly'] = array_fill(0, ($nroots + 1), 0);
+ $rs['fcr'] = $fcr;
+ $rs['prim'] = $prim;
+ $rs['nroots'] = $nroots;
+ $rs['gfpoly'] = $gfpoly;
+ // Find prim-th root of 1, used in decoding
+ for ($iprim=1; ($iprim % $prim) != 0; $iprim += $rs['nn']) {
+ ; // intentional empty-body loop!
+ }
+ $rs['iprim'] = (int)($iprim / $prim);
+ $rs['genpoly'][0] = 1;
+
+
+ for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
+ $rs['genpoly'][$i+1] = 1;
+ // Multiply rs->genpoly[] by @**(root + x)
+ for ($j = $i; $j > 0; --$j) {
+ if ($rs['genpoly'][$j] != 0) {
+ $rs['genpoly'][$j] = $rs['genpoly'][$j-1] ^ $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][$j]] + $root)];
+ } else {
+ $rs['genpoly'][$j] = $rs['genpoly'][$j-1];
+ }
+ }
+ // rs->genpoly[0] can never be zero
+ $rs['genpoly'][0] = $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][0]] + $root)];
+ }
+ // convert rs->genpoly[] to index form for quicker encoding
+ for ($i = 0; $i <= $nroots; ++$i) {
+ $rs['genpoly'][$i] = $rs['index_of'][$rs['genpoly'][$i]];
+ }
+ return $rs;
+ }
+
+ /**
+ * Encode a Reed-Solomon codec and returns the parity array
+ * @param array $rs RS values
+ * @param array $data data
+ * @param array $parity parity
+ * @return parity array
+ */
+ protected function encode_rs_char($rs, $data, $parity) {
+ $MM =& $rs['mm']; // bits per symbol
+ $NN =& $rs['nn']; // the total number of symbols in a RS block
+ $ALPHA_TO =& $rs['alpha_to']; // the address of an array of NN elements to convert Galois field elements in index (log) form to polynomial form
+ $INDEX_OF =& $rs['index_of']; // the address of an array of NN elements to convert Galois field elements in polynomial form to index (log) form
+ $GENPOLY =& $rs['genpoly']; // an array of NROOTS+1 elements containing the generator polynomial in index form
+ $NROOTS =& $rs['nroots']; // the number of roots in the RS code generator polynomial, which is the same as the number of parity symbols in a block
+ $FCR =& $rs['fcr']; // first consecutive root, index form
+ $PRIM =& $rs['prim']; // primitive element, index form
+ $IPRIM =& $rs['iprim']; // prim-th root of 1, index form
+ $PAD =& $rs['pad']; // the number of pad symbols in a block
+ $A0 =& $NN;
+ $parity = array_fill(0, $NROOTS, 0);
+ for ($i=0; $i < ($NN - $NROOTS - $PAD); $i++) {
+ $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
+ if ($feedback != $A0) {
+ // feedback term is non-zero
+ // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
+ // always be for the polynomials constructed by init_rs()
+ $feedback = $this->modnn($rs, $NN - $GENPOLY[$NROOTS] + $feedback);
+ for ($j=1; $j < $NROOTS; ++$j) {
+ $parity[$j] ^= $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[($NROOTS - $j)])];
+ }
+ }
+ // Shift
+ array_shift($parity);
+ if ($feedback != $A0) {
+ array_push($parity, $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[0])]);
+ } else {
+ array_push($parity, 0);
+ }
+ }
+ return $parity;
+ }
+
+ } // end QRcode class
+
+} // END OF "class_exists QRcode"
+?>
diff --git a/app/lib/phpqrcode/index.php b/app/lib/phpqrcode/index.php
new file mode 100644
index 00000000..9e14b7ea
--- /dev/null
+++ b/app/lib/phpqrcode/index.php
@@ -0,0 +1,94 @@
+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ echo "PHP QR Code ";
+
+ //set it to writable location, a place for temp generated PNG files
+ $PNG_TEMP_DIR = dirname(__FILE__).DIRECTORY_SEPARATOR.'temp'.DIRECTORY_SEPARATOR;
+
+ //html PNG location prefix
+ $PNG_WEB_DIR = 'temp/';
+
+ include "qrlib.php";
+
+ //ofcourse we need rights to create temp dir
+ if (!file_exists($PNG_TEMP_DIR))
+ mkdir($PNG_TEMP_DIR);
+
+
+ $filename = $PNG_TEMP_DIR.'test.png';
+
+ //processing form input
+ //remember to sanitize user input in real-life solution !!!
+ $errorCorrectionLevel = 'L';
+ if (isset($_REQUEST['level']) && in_array($_REQUEST['level'], array('L','M','Q','H')))
+ $errorCorrectionLevel = $_REQUEST['level'];
+
+ $matrixPointSize = 4;
+ if (isset($_REQUEST['size']))
+ $matrixPointSize = min(max((int)$_REQUEST['size'], 1), 10);
+
+
+ if (isset($_REQUEST['data'])) {
+
+ //it's very important!
+ if (trim($_REQUEST['data']) == '')
+ die('data cannot be empty! back ');
+
+ // user data
+ $filename = $PNG_TEMP_DIR.'test'.md5($_REQUEST['data'].'|'.$errorCorrectionLevel.'|'.$matrixPointSize).'.png';
+ QRcode::png($_REQUEST['data'], $filename, $errorCorrectionLevel, $matrixPointSize, 2);
+
+ } else {
+
+ //default data
+ echo 'You can provide data in GET parameter: like that ';
+ QRcode::png('PHP QR Code :)', $filename, $errorCorrectionLevel, $matrixPointSize, 2);
+
+ }
+
+ //display generated file
+ echo ' ';
+
+ //config form
+ echo ' ';
+
+ // benchmark
+ QRtools::timeBenchmark();
+
+
\ No newline at end of file
diff --git a/app/lib/phpqrcode/phpqrcode.php b/app/lib/phpqrcode/phpqrcode.php
new file mode 100644
index 00000000..80adb9df
--- /dev/null
+++ b/app/lib/phpqrcode/phpqrcode.php
@@ -0,0 +1,3312 @@
+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+
+/*
+ * Version: 1.1.4
+ * Build: 2010100721
+ */
+
+
+
+//---- qrconst.php -----------------------------
+
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Common constants
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ // Encoding modes
+
+ define('QR_MODE_NUL', -1);
+ define('QR_MODE_NUM', 0);
+ define('QR_MODE_AN', 1);
+ define('QR_MODE_8', 2);
+ define('QR_MODE_KANJI', 3);
+ define('QR_MODE_STRUCTURE', 4);
+
+ // Levels of error correction.
+
+ define('QR_ECLEVEL_L', 0);
+ define('QR_ECLEVEL_M', 1);
+ define('QR_ECLEVEL_Q', 2);
+ define('QR_ECLEVEL_H', 3);
+
+ // Supported output formats
+
+ define('QR_FORMAT_TEXT', 0);
+ define('QR_FORMAT_PNG', 1);
+
+ class qrstr {
+ public static function set(&$srctab, $x, $y, $repl, $replLen = false) {
+ $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
+ }
+ }
+
+
+
+//---- merged_config.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Config file, tuned-up for merged verion
+ */
+
+ define('QR_CACHEABLE', false); // use cache - more disk reads but less CPU power, masks and format templates are stored there
+ define('QR_CACHE_DIR', false); // used when QR_CACHEABLE === true
+ define('QR_LOG_DIR', false); // default error logs dir
+
+ define('QR_FIND_BEST_MASK', true); // if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code
+ define('QR_FIND_FROM_RANDOM', 2); // if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly
+ define('QR_DEFAULT_MASK', 2); // when QR_FIND_BEST_MASK === false
+
+ define('QR_PNG_MAXIMUM_SIZE', 1024); // maximum allowed png image width (in pixels), tune to make sure GD and PHP can handle such big images
+
+
+
+
+//---- qrtools.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Toolset, handy and debug utilites.
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRtools {
+
+ //----------------------------------------------------------------------
+ public static function binarize($frame)
+ {
+ $len = count($frame);
+ foreach ($frame as &$frameLine) {
+
+ for($i=0; $i<$len; $i++) {
+ $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
+ }
+ }
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function tcpdfBarcodeArray($code, $mode = 'QR,L', $tcPdfVersion = '4.5.037')
+ {
+ $barcode_array = array();
+
+ if (!is_array($mode))
+ $mode = explode(',', $mode);
+
+ $eccLevel = 'L';
+
+ if (count($mode) > 1) {
+ $eccLevel = $mode[1];
+ }
+
+ $qrTab = QRcode::text($code, false, $eccLevel);
+ $size = count($qrTab);
+
+ $barcode_array['num_rows'] = $size;
+ $barcode_array['num_cols'] = $size;
+ $barcode_array['bcode'] = array();
+
+ foreach ($qrTab as $line) {
+ $arrAdd = array();
+ foreach(str_split($line) as $char)
+ $arrAdd[] = ($char=='1')?1:0;
+ $barcode_array['bcode'][] = $arrAdd;
+ }
+
+ return $barcode_array;
+ }
+
+ //----------------------------------------------------------------------
+ public static function clearCache()
+ {
+ self::$frames = array();
+ }
+
+ //----------------------------------------------------------------------
+ public static function buildCache()
+ {
+ QRtools::markTime('before_build_cache');
+
+ $mask = new QRmask();
+ for ($a=1; $a <= QRSPEC_VERSION_MAX; $a++) {
+ $frame = QRspec::newFrame($a);
+ if (QR_IMAGE) {
+ $fileName = QR_CACHE_DIR.'frame_'.$a.'.png';
+ QRimage::png(self::binarize($frame), $fileName, 1, 0);
+ }
+
+ $width = count($frame);
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+ for ($maskNo=0; $maskNo<8; $maskNo++)
+ $mask->makeMaskNo($maskNo, $width, $frame, $bitMask, true);
+ }
+
+ QRtools::markTime('after_build_cache');
+ }
+
+ //----------------------------------------------------------------------
+ public static function log($outfile, $err)
+ {
+ if (QR_LOG_DIR !== false) {
+ if ($err != '') {
+ if ($outfile !== false) {
+ file_put_contents(QR_LOG_DIR.basename($outfile).'-errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ } else {
+ file_put_contents(QR_LOG_DIR.'errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function dumpMask($frame)
+ {
+ $width = count($frame);
+ for($y=0;$y<$width;$y++) {
+ for($x=0;$x<$width;$x++) {
+ echo ord($frame[$y][$x]).',';
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function markTime($markerId)
+ {
+ list($usec, $sec) = explode(" ", microtime());
+ $time = ((float)$usec + (float)$sec);
+
+ if (!isset($GLOBALS['qr_time_bench']))
+ $GLOBALS['qr_time_bench'] = array();
+
+ $GLOBALS['qr_time_bench'][$markerId] = $time;
+ }
+
+ //----------------------------------------------------------------------
+ public static function timeBenchmark()
+ {
+ self::markTime('finish');
+
+ $lastTime = 0;
+ $startTime = 0;
+ $p = 0;
+
+ echo '
+ BENCHMARK
+ ';
+
+ foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) {
+ if ($p > 0) {
+ echo 'till '.$markerId.': '.number_format($thisTime-$lastTime, 6).'s ';
+ } else {
+ $startTime = $thisTime;
+ }
+
+ $p++;
+ $lastTime = $thisTime;
+ }
+
+ echo '
+ TOTAL: '.number_format($lastTime-$startTime, 6).'s
+
+
';
+ }
+
+ }
+
+ //##########################################################################
+
+ QRtools::markTime('start');
+
+
+
+
+//---- qrspec.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * QR Code specifications
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('QRSPEC_VERSION_MAX', 40);
+ define('QRSPEC_WIDTH_MAX', 177);
+
+ define('QRCAP_WIDTH', 0);
+ define('QRCAP_WORDS', 1);
+ define('QRCAP_REMINDER', 2);
+ define('QRCAP_EC', 3);
+
+ class QRspec {
+
+ public static $capacity = array(
+ array( 0, 0, 0, array( 0, 0, 0, 0)),
+ array( 21, 26, 0, array( 7, 10, 13, 17)), // 1
+ array( 25, 44, 7, array( 10, 16, 22, 28)),
+ array( 29, 70, 7, array( 15, 26, 36, 44)),
+ array( 33, 100, 7, array( 20, 36, 52, 64)),
+ array( 37, 134, 7, array( 26, 48, 72, 88)), // 5
+ array( 41, 172, 7, array( 36, 64, 96, 112)),
+ array( 45, 196, 0, array( 40, 72, 108, 130)),
+ array( 49, 242, 0, array( 48, 88, 132, 156)),
+ array( 53, 292, 0, array( 60, 110, 160, 192)),
+ array( 57, 346, 0, array( 72, 130, 192, 224)), //10
+ array( 61, 404, 0, array( 80, 150, 224, 264)),
+ array( 65, 466, 0, array( 96, 176, 260, 308)),
+ array( 69, 532, 0, array( 104, 198, 288, 352)),
+ array( 73, 581, 3, array( 120, 216, 320, 384)),
+ array( 77, 655, 3, array( 132, 240, 360, 432)), //15
+ array( 81, 733, 3, array( 144, 280, 408, 480)),
+ array( 85, 815, 3, array( 168, 308, 448, 532)),
+ array( 89, 901, 3, array( 180, 338, 504, 588)),
+ array( 93, 991, 3, array( 196, 364, 546, 650)),
+ array( 97, 1085, 3, array( 224, 416, 600, 700)), //20
+ array(101, 1156, 4, array( 224, 442, 644, 750)),
+ array(105, 1258, 4, array( 252, 476, 690, 816)),
+ array(109, 1364, 4, array( 270, 504, 750, 900)),
+ array(113, 1474, 4, array( 300, 560, 810, 960)),
+ array(117, 1588, 4, array( 312, 588, 870, 1050)), //25
+ array(121, 1706, 4, array( 336, 644, 952, 1110)),
+ array(125, 1828, 4, array( 360, 700, 1020, 1200)),
+ array(129, 1921, 3, array( 390, 728, 1050, 1260)),
+ array(133, 2051, 3, array( 420, 784, 1140, 1350)),
+ array(137, 2185, 3, array( 450, 812, 1200, 1440)), //30
+ array(141, 2323, 3, array( 480, 868, 1290, 1530)),
+ array(145, 2465, 3, array( 510, 924, 1350, 1620)),
+ array(149, 2611, 3, array( 540, 980, 1440, 1710)),
+ array(153, 2761, 3, array( 570, 1036, 1530, 1800)),
+ array(157, 2876, 0, array( 570, 1064, 1590, 1890)), //35
+ array(161, 3034, 0, array( 600, 1120, 1680, 1980)),
+ array(165, 3196, 0, array( 630, 1204, 1770, 2100)),
+ array(169, 3362, 0, array( 660, 1260, 1860, 2220)),
+ array(173, 3532, 0, array( 720, 1316, 1950, 2310)),
+ array(177, 3706, 0, array( 750, 1372, 2040, 2430)) //40
+ );
+
+ //----------------------------------------------------------------------
+ public static function getDataLength($version, $level)
+ {
+ return self::$capacity[$version][QRCAP_WORDS] - self::$capacity[$version][QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getECCLength($version, $level)
+ {
+ return self::$capacity[$version][QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getWidth($version)
+ {
+ return self::$capacity[$version][QRCAP_WIDTH];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getRemainder($version)
+ {
+ return self::$capacity[$version][QRCAP_REMINDER];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getMinimumVersion($size, $level)
+ {
+
+ for($i=1; $i<= QRSPEC_VERSION_MAX; $i++) {
+ $words = self::$capacity[$i][QRCAP_WORDS] - self::$capacity[$i][QRCAP_EC][$level];
+ if($words >= $size)
+ return $i;
+ }
+
+ return -1;
+ }
+
+ //######################################################################
+
+ public static $lengthTableBits = array(
+ array(10, 12, 14),
+ array( 9, 11, 13),
+ array( 8, 16, 16),
+ array( 8, 10, 12)
+ );
+
+ //----------------------------------------------------------------------
+ public static function lengthIndicator($mode, $version)
+ {
+ if ($mode == QR_MODE_STRUCTURE)
+ return 0;
+
+ if ($version <= 9) {
+ $l = 0;
+ } else if ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ return self::$lengthTableBits[$mode][$l];
+ }
+
+ //----------------------------------------------------------------------
+ public static function maximumWords($mode, $version)
+ {
+ if($mode == QR_MODE_STRUCTURE)
+ return 3;
+
+ if($version <= 9) {
+ $l = 0;
+ } else if($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ $bits = self::$lengthTableBits[$mode][$l];
+ $words = (1 << $bits) - 1;
+
+ if($mode == QR_MODE_KANJI) {
+ $words *= 2; // the number of bytes is required
+ }
+
+ return $words;
+ }
+
+ // Error correction code -----------------------------------------------
+ // Table of the error correction code (Reed-Solomon block)
+ // See Table 12-16 (pp.30-36), JIS X0510:2004.
+
+ public static $eccTable = array(
+ array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)),
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)),
+ array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5
+ array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)),
+ array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)),
+ array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)),
+ array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)),
+ array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), //10
+ array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)),
+ array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)),
+ array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)),
+ array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)),
+ array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), //15
+ array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)),
+ array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)),
+ array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)),
+ array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)),
+ array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), //20
+ array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)),
+ array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)),
+ array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)),
+ array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)),
+ array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), //25
+ array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)),
+ array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)),
+ array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)),
+ array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)),
+ array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), //30
+ array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)),
+ array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)),
+ array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)),
+ array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)),
+ array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), //35
+ array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)),
+ array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)),
+ array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)),
+ array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)),
+ array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),//40
+ );
+
+ //----------------------------------------------------------------------
+ // CACHEABLE!!!
+
+ public static function getEccSpec($version, $level, array &$spec)
+ {
+ if (count($spec) < 5) {
+ $spec = array(0,0,0,0,0);
+ }
+
+ $b1 = self::$eccTable[$version][$level][0];
+ $b2 = self::$eccTable[$version][$level][1];
+ $data = self::getDataLength($version, $level);
+ $ecc = self::getECCLength($version, $level);
+
+ if($b2 == 0) {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / $b1);
+ $spec[2] = (int)($ecc / $b1);
+ $spec[3] = 0;
+ $spec[4] = 0;
+ } else {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / ($b1 + $b2));
+ $spec[2] = (int)($ecc / ($b1 + $b2));
+ $spec[3] = $b2;
+ $spec[4] = $spec[1] + 1;
+ }
+ }
+
+ // Alignment pattern ---------------------------------------------------
+
+ // Positions of alignment patterns.
+ // This array includes only the second and the third position of the
+ // alignment patterns. Rest of them can be calculated from the distance
+ // between them.
+
+ // See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
+
+ public static $alignmentPattern = array(
+ array( 0, 0),
+ array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5
+ array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10
+ array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), //11-15
+ array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), //16-20
+ array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), //21-25
+ array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), //26-30
+ array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), //31-35
+ array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40
+ );
+
+
+ /** --------------------------------------------------------------------
+ * Put an alignment marker.
+ * @param frame
+ * @param width
+ * @param ox,oy center coordinate of the pattern
+ */
+ public static function putAlignmentMarker(array &$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xa1\xa1\xa1\xa1\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa0\xa1\xa0\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa1\xa1\xa1\xa1"
+ );
+
+ $yStart = $oy-2;
+ $xStart = $ox-2;
+
+ for($y=0; $y<5; $y++) {
+ QRstr::set($frame, $xStart, $yStart+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function putAlignmentPattern($version, &$frame, $width)
+ {
+ if($version < 2)
+ return;
+
+ $d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0];
+ if($d < 0) {
+ $w = 2;
+ } else {
+ $w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2);
+ }
+
+ if($w * $w - 3 == 1) {
+ $x = self::$alignmentPattern[$version][0];
+ $y = self::$alignmentPattern[$version][0];
+ self::putAlignmentMarker($frame, $x, $y);
+ return;
+ }
+
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=1; $x<$w - 1; $x++) {
+ self::putAlignmentMarker($frame, 6, $cx);
+ self::putAlignmentMarker($frame, $cx, 6);
+ $cx += $d;
+ }
+
+ $cy = self::$alignmentPattern[$version][0];
+ for($y=0; $y<$w-1; $y++) {
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=0; $x<$w-1; $x++) {
+ self::putAlignmentMarker($frame, $cx, $cy);
+ $cx += $d;
+ }
+ $cy += $d;
+ }
+ }
+
+ // Version information pattern -----------------------------------------
+
+ // Version information pattern (BCH coded).
+ // See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
+
+ // size: [QRSPEC_VERSION_MAX - 6]
+
+ public static $versionPattern = array(
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
+ 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
+ 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
+ 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
+ 0x27541, 0x28c69
+ );
+
+ //----------------------------------------------------------------------
+ public static function getVersionPattern($version)
+ {
+ if($version < 7 || $version > QRSPEC_VERSION_MAX)
+ return 0;
+
+ return self::$versionPattern[$version -7];
+ }
+
+ // Format information --------------------------------------------------
+ // See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib)
+
+ public static $formatInfo = array(
+ array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976),
+ array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0),
+ array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed),
+ array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b)
+ );
+
+ public static function getFormatInfo($mask, $level)
+ {
+ if($mask < 0 || $mask > 7)
+ return 0;
+
+ if($level < 0 || $level > 3)
+ return 0;
+
+ return self::$formatInfo[$level][$mask];
+ }
+
+ // Frame ---------------------------------------------------------------
+ // Cache of initial frames.
+
+ public static $frames = array();
+
+ /** --------------------------------------------------------------------
+ * Put a finder pattern.
+ * @param frame
+ * @param width
+ * @param ox,oy upper-left coordinate of the pattern
+ */
+ public static function putFinderPattern(&$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
+ );
+
+ for($y=0; $y<7; $y++) {
+ QRstr::set($frame, $ox, $oy+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function createFrame($version)
+ {
+ $width = self::$capacity[$version][QRCAP_WIDTH];
+ $frameLine = str_repeat ("\0", $width);
+ $frame = array_fill(0, $width, $frameLine);
+
+ // Finder pattern
+ self::putFinderPattern($frame, 0, 0);
+ self::putFinderPattern($frame, $width - 7, 0);
+ self::putFinderPattern($frame, 0, $width - 7);
+
+ // Separator
+ $yOffset = $width - 7;
+
+ for($y=0; $y<7; $y++) {
+ $frame[$y][7] = "\xc0";
+ $frame[$y][$width - 8] = "\xc0";
+ $frame[$yOffset][7] = "\xc0";
+ $yOffset++;
+ }
+
+ $setPattern = str_repeat("\xc0", 8);
+
+ QRstr::set($frame, 0, 7, $setPattern);
+ QRstr::set($frame, $width-8, 7, $setPattern);
+ QRstr::set($frame, 0, $width - 8, $setPattern);
+
+ // Format info
+ $setPattern = str_repeat("\x84", 9);
+ QRstr::set($frame, 0, 8, $setPattern);
+ QRstr::set($frame, $width - 8, 8, $setPattern, 8);
+
+ $yOffset = $width - 8;
+
+ for($y=0; $y<8; $y++,$yOffset++) {
+ $frame[$y][8] = "\x84";
+ $frame[$yOffset][8] = "\x84";
+ }
+
+ // Timing pattern
+
+ for($i=1; $i<$width-15; $i++) {
+ $frame[6][7+$i] = chr(0x90 | ($i & 1));
+ $frame[7+$i][6] = chr(0x90 | ($i & 1));
+ }
+
+ // Alignment pattern
+ self::putAlignmentPattern($version, $frame, $width);
+
+ // Version information
+ if($version >= 7) {
+ $vinf = self::getVersionPattern($version);
+
+ $v = $vinf;
+
+ for($x=0; $x<6; $x++) {
+ for($y=0; $y<3; $y++) {
+ $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+
+ $v = $vinf;
+ for($y=0; $y<6; $y++) {
+ for($x=0; $x<3; $x++) {
+ $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+ }
+
+ // and a little bit...
+ $frame[$width - 8][8] = "\x81";
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function debug($frame, $binary_mode = false)
+ {
+ if ($binary_mode) {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join(' ', explode('0', $frameLine));
+ $frameLine = join('██', explode('1', $frameLine));
+ }
+
+ ?>
+
+ ';
+ echo join(" ", $frame);
+ echo ' ';
+
+ } else {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join(' ', explode("\xc0", $frameLine));
+ $frameLine = join('▒ ', explode("\xc1", $frameLine));
+ $frameLine = join(' ', explode("\xa0", $frameLine));
+ $frameLine = join('▒ ', explode("\xa1", $frameLine));
+ $frameLine = join('◇ ', explode("\x84", $frameLine)); //format 0
+ $frameLine = join('◆ ', explode("\x85", $frameLine)); //format 1
+ $frameLine = join('☢ ', explode("\x81", $frameLine)); //special bit
+ $frameLine = join(' ', explode("\x90", $frameLine)); //clock 0
+ $frameLine = join('◷ ', explode("\x91", $frameLine)); //clock 1
+ $frameLine = join(' ', explode("\x88", $frameLine)); //version
+ $frameLine = join('▒ ', explode("\x89", $frameLine)); //version
+ $frameLine = join('♦', explode("\x01", $frameLine));
+ $frameLine = join('⋅', explode("\0", $frameLine));
+ }
+
+ ?>
+
+ ";
+ echo join(" ", $frame);
+ echo " ";
+
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($frame)
+ {
+ return gzcompress(join("\n", $frame), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ return explode("\n", gzuncompress($code));
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFrame($version)
+ {
+ if($version < 1 || $version > QRSPEC_VERSION_MAX)
+ return null;
+
+ if(!isset(self::$frames[$version])) {
+
+ $fileName = QR_CACHE_DIR.'frame_'.$version.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ self::$frames[$version] = self::unserial(file_get_contents($fileName));
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ file_put_contents($fileName, self::serial(self::$frames[$version]));
+ }
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ }
+ }
+
+ if(is_null(self::$frames[$version]))
+ return null;
+
+ return self::$frames[$version];
+ }
+
+ //----------------------------------------------------------------------
+ public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; }
+ public static function rsBlockNum1($spec) { return $spec[0]; }
+ public static function rsDataCodes1($spec) { return $spec[1]; }
+ public static function rsEccCodes1($spec) { return $spec[2]; }
+ public static function rsBlockNum2($spec) { return $spec[3]; }
+ public static function rsDataCodes2($spec) { return $spec[4]; }
+ public static function rsEccCodes2($spec) { return $spec[2]; }
+ public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); }
+ public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; }
+
+ }
+
+
+
+//---- qrimage.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Image output of code using GD2
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('QR_IMAGE', true);
+
+ class QRimage {
+
+ //----------------------------------------------------------------------
+ public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/png");
+ ImagePng($image);
+ } else {
+ if($saveandprint===TRUE){
+ ImagePng($image, $filename);
+ header("Content-type: image/png");
+ ImagePng($image);
+ }else{
+ ImagePng($image, $filename);
+ }
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/jpeg");
+ ImageJpeg($image, null, $q);
+ } else {
+ ImageJpeg($image, $filename, $q);
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ private static function image($frame, $pixelPerPoint = 4, $outerFrame = 4)
+ {
+ $h = count($frame);
+ $w = strlen($frame[0]);
+
+ $imgW = $w + 2*$outerFrame;
+ $imgH = $h + 2*$outerFrame;
+
+ $base_image =ImageCreate($imgW, $imgH);
+
+ $col[0] = ImageColorAllocate($base_image,255,255,255);
+ $col[1] = ImageColorAllocate($base_image,0,0,0);
+
+ imagefill($base_image, 0, 0, $col[0]);
+
+ for($y=0; $y<$h; $y++) {
+ for($x=0; $x<$w; $x++) {
+ if ($frame[$y][$x] == '1') {
+ ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]);
+ }
+ }
+ }
+
+ $target_image =ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint);
+ ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH);
+ ImageDestroy($base_image);
+
+ return $target_image;
+ }
+ }
+
+
+
+//---- qrinput.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Input encoding class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('STRUCTURE_HEADER_BITS', 20);
+ define('MAX_STRUCTURED_SYMBOLS', 16);
+
+ class QRinputItem {
+
+ public $mode;
+ public $size;
+ public $data;
+ public $bstream;
+
+ public function __construct($mode, $size, $data, $bstream = null)
+ {
+ $setData = array_slice($data, 0, $size);
+
+ if (count($setData) < $size) {
+ $setData = array_merge($setData, array_fill(0,$size-count($setData),0));
+ }
+
+ if(!QRinput::check($mode, $size, $setData)) {
+ throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData));
+ return null;
+ }
+
+ $this->mode = $mode;
+ $this->size = $size;
+ $this->data = $setData;
+ $this->bstream = $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeNum($version)
+ {
+ try {
+
+ $words = (int)($this->size / 3);
+ $bs = new QRbitstream();
+
+ $val = 0x1;
+ $bs->appendNum(4, $val);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (ord($this->data[$i*3 ]) - ord('0')) * 100;
+ $val += (ord($this->data[$i*3+1]) - ord('0')) * 10;
+ $val += (ord($this->data[$i*3+2]) - ord('0'));
+ $bs->appendNum(10, $val);
+ }
+
+ if($this->size - $words * 3 == 1) {
+ $val = ord($this->data[$words*3]) - ord('0');
+ $bs->appendNum(4, $val);
+ } else if($this->size - $words * 3 == 2) {
+ $val = (ord($this->data[$words*3 ]) - ord('0')) * 10;
+ $val += (ord($this->data[$words*3+1]) - ord('0'));
+ $bs->appendNum(7, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeAn($version)
+ {
+ try {
+ $words = (int)($this->size / 2);
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x02);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45;
+ $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1]));
+
+ $bs->appendNum(11, $val);
+ }
+
+ if($this->size & 1) {
+ $val = QRinput::lookAnTable(ord($this->data[$words * 2]));
+ $bs->appendNum(6, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeMode8($version)
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x4);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size);
+
+ for($i=0; $i<$this->size; $i++) {
+ $bs->appendNum(8, ord($this->data[$i]));
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeKanji($version)
+ {
+ try {
+
+ $bs = new QRbitrtream();
+
+ $bs->appendNum(4, 0x8);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2));
+
+ for($i=0; $i<$this->size; $i+=2) {
+ $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);
+ if($val <= 0x9ffc) {
+ $val -= 0x8140;
+ } else {
+ $val -= 0xc140;
+ }
+
+ $h = ($val >> 8) * 0xc0;
+ $val = ($val & 0xff) + $h;
+
+ $bs->appendNum(13, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeStructure()
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x03);
+ $bs->appendNum(4, ord($this->data[1]) - 1);
+ $bs->appendNum(4, ord($this->data[0]) - 1);
+ $bs->appendNum(8, ord($this->data[2]));
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSizeOfEntry($version)
+ {
+ $bits = 0;
+
+ if($version == 0)
+ $version = 1;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break;
+ case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break;
+ case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break;
+ case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break;
+ case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS;
+ default:
+ return 0;
+ }
+
+ $l = QRspec::lengthIndicator($this->mode, $version);
+ $m = 1 << $l;
+ $num = (int)(($this->size + $m - 1) / $m);
+
+ $bits += $num * (4 + $l);
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeBitStream($version)
+ {
+ try {
+
+ unset($this->bstream);
+ $words = QRspec::maximumWords($this->mode, $version);
+
+ if($this->size > $words) {
+
+ $st1 = new QRinputItem($this->mode, $words, $this->data);
+ $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words));
+
+ $st1->encodeBitStream($version);
+ $st2->encodeBitStream($version);
+
+ $this->bstream = new QRbitstream();
+ $this->bstream->append($st1->bstream);
+ $this->bstream->append($st2->bstream);
+
+ unset($st1);
+ unset($st2);
+
+ } else {
+
+ $ret = 0;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break;
+ case QR_MODE_AN: $ret = $this->encodeModeAn($version); break;
+ case QR_MODE_8: $ret = $this->encodeMode8($version); break;
+ case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break;
+ case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break;
+
+ default:
+ break;
+ }
+
+ if($ret < 0)
+ return -1;
+ }
+
+ return $this->bstream->size();
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+ };
+
+ //##########################################################################
+
+ class QRinput {
+
+ public $items;
+
+ private $version;
+ private $level;
+
+ //----------------------------------------------------------------------
+ public function __construct($version = 0, $level = QR_ECLEVEL_L)
+ {
+ if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid version no');
+ return NULL;
+ }
+
+ $this->version = $version;
+ $this->level = $level;
+ }
+
+ //----------------------------------------------------------------------
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ //----------------------------------------------------------------------
+ public function setVersion($version)
+ {
+ if($version < 0 || $version > QRSPEC_VERSION_MAX) {
+ throw new Exception('Invalid version no');
+ return -1;
+ }
+
+ $this->version = $version;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getErrorCorrectionLevel()
+ {
+ return $this->level;
+ }
+
+ //----------------------------------------------------------------------
+ public function setErrorCorrectionLevel($level)
+ {
+ if($level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid ECLEVEL');
+ return -1;
+ }
+
+ $this->level = $level;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendEntry(QRinputItem $entry)
+ {
+ $this->items[] = $entry;
+ }
+
+ //----------------------------------------------------------------------
+ public function append($mode, $size, $data)
+ {
+ try {
+ $entry = new QRinputItem($mode, $size, $data);
+ $this->items[] = $entry;
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+
+ public function insertStructuredAppendHeader($size, $index, $parity)
+ {
+ if( $size > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong size');
+ }
+
+ if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong index');
+ }
+
+ $buf = array($size, $index, $parity);
+
+ try {
+ $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf);
+ array_unshift($this->items, $entry);
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function calcParity()
+ {
+ $parity = 0;
+
+ foreach($this->items as $item) {
+ if($item->mode != QR_MODE_STRUCTURE) {
+ for($i=$item->size-1; $i>=0; $i--) {
+ $parity ^= $item->data[$i];
+ }
+ }
+ }
+
+ return $parity;
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeNum($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeNum($size)
+ {
+ $w = (int)$size / 3;
+ $bits = $w * 10;
+
+ switch($size - $w * 3) {
+ case 1:
+ $bits += 4;
+ break;
+ case 2:
+ $bits += 7;
+ break;
+ default:
+ break;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static $anTable = array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ );
+
+ //----------------------------------------------------------------------
+ public static function lookAnTable($c)
+ {
+ return (($c > 127)?-1:self::$anTable[$c]);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeAn($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if (self::lookAnTable(ord($data[$i])) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeAn($size)
+ {
+ $w = (int)($size / 2);
+ $bits = $w * 11;
+
+ if($size & 1) {
+ $bits += 6;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsMode8($size)
+ {
+ return $size * 8;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitsModeKanji($size)
+ {
+ return (int)(($size / 2) * 13);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeKanji($size, $data)
+ {
+ if($size & 1)
+ return false;
+
+ for($i=0; $i<$size; $i+=2) {
+ $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
+ if( $val < 0x8140
+ || ($val > 0x9ffc && $val < 0xe040)
+ || $val > 0xebbf) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /***********************************************************************
+ * Validation
+ **********************************************************************/
+
+ public static function check($mode, $size, $data)
+ {
+ if($size <= 0)
+ return false;
+
+ switch($mode) {
+ case QR_MODE_NUM: return self::checkModeNum($size, $data); break;
+ case QR_MODE_AN: return self::checkModeAn($size, $data); break;
+ case QR_MODE_KANJI: return self::checkModeKanji($size, $data); break;
+ case QR_MODE_8: return true; break;
+ case QR_MODE_STRUCTURE: return true; break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSize($version)
+ {
+ $bits = 0;
+
+ foreach($this->items as $item) {
+ $bits += $item->estimateBitStreamSizeOfEntry($version);
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateVersion()
+ {
+ $version = 0;
+ $prev = 0;
+ do {
+ $prev = $version;
+ $bits = $this->estimateBitStreamSize($prev);
+ $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($version < 0) {
+ return -1;
+ }
+ } while ($version > $prev);
+
+ return $version;
+ }
+
+ //----------------------------------------------------------------------
+ public static function lengthOfCode($mode, $version, $bits)
+ {
+ $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);
+ switch($mode) {
+ case QR_MODE_NUM:
+ $chunks = (int)($payload / 10);
+ $remain = $payload - $chunks * 10;
+ $size = $chunks * 3;
+ if($remain >= 7) {
+ $size += 2;
+ } else if($remain >= 4) {
+ $size += 1;
+ }
+ break;
+ case QR_MODE_AN:
+ $chunks = (int)($payload / 11);
+ $remain = $payload - $chunks * 11;
+ $size = $chunks * 2;
+ if($remain >= 6)
+ $size++;
+ break;
+ case QR_MODE_8:
+ $size = (int)($payload / 8);
+ break;
+ case QR_MODE_KANJI:
+ $size = (int)(($payload / 13) * 2);
+ break;
+ case QR_MODE_STRUCTURE:
+ $size = (int)($payload / 8);
+ break;
+ default:
+ $size = 0;
+ break;
+ }
+
+ $maxsize = QRspec::maximumWords($mode, $version);
+ if($size < 0) $size = 0;
+ if($size > $maxsize) $size = $maxsize;
+
+ return $size;
+ }
+
+ //----------------------------------------------------------------------
+ public function createBitStream()
+ {
+ $total = 0;
+
+ foreach($this->items as $item) {
+ $bits = $item->encodeBitStream($this->version);
+
+ if($bits < 0)
+ return -1;
+
+ $total += $bits;
+ }
+
+ return $total;
+ }
+
+ //----------------------------------------------------------------------
+ public function convertData()
+ {
+ $ver = $this->estimateVersion();
+ if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ }
+
+ for(;;) {
+ $bits = $this->createBitStream();
+
+ if($bits < 0)
+ return -1;
+
+ $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if($ver < 0) {
+ throw new Exception('WRONG VERSION');
+ return -1;
+ } else if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ } else {
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendPaddingBit(&$bstream)
+ {
+ $bits = $bstream->size();
+ $maxwords = QRspec::getDataLength($this->version, $this->level);
+ $maxbits = $maxwords * 8;
+
+ if ($maxbits == $bits) {
+ return 0;
+ }
+
+ if ($maxbits - $bits < 5) {
+ return $bstream->appendNum($maxbits - $bits, 0);
+ }
+
+ $bits += 4;
+ $words = (int)(($bits + 7) / 8);
+
+ $padding = new QRbitstream();
+ $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
+
+ if($ret < 0)
+ return $ret;
+
+ $padlen = $maxwords - $words;
+
+ if($padlen > 0) {
+
+ $padbuf = array();
+ for($i=0; $i<$padlen; $i++) {
+ $padbuf[$i] = ($i&1)?0x11:0xec;
+ }
+
+ $ret = $padding->appendBytes($padlen, $padbuf);
+
+ if($ret < 0)
+ return $ret;
+
+ }
+
+ $ret = $bstream->append($padding);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function mergeBitStream()
+ {
+ if($this->convertData() < 0) {
+ return null;
+ }
+
+ $bstream = new QRbitstream();
+
+ foreach($this->items as $item) {
+ $ret = $bstream->append($item->bstream);
+ if($ret < 0) {
+ return null;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getBitStream()
+ {
+
+ $bstream = $this->mergeBitStream();
+
+ if($bstream == null) {
+ return null;
+ }
+
+ $ret = $this->appendPaddingBit($bstream);
+ if($ret < 0) {
+ return null;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getByteStream()
+ {
+ $bstream = $this->getBitStream();
+ if($bstream == null) {
+ return null;
+ }
+
+ return $bstream->toByte();
+ }
+ }
+
+
+
+
+
+
+//---- qrbitstream.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Bitstream class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRbitstream {
+
+ public $data = array();
+
+ //----------------------------------------------------------------------
+ public function size()
+ {
+ return count($this->data);
+ }
+
+ //----------------------------------------------------------------------
+ public function allocate($setLength)
+ {
+ $this->data = array_fill(0, $setLength, 0);
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromNum($bits, $num)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($bits);
+
+ $mask = 1 << ($bits - 1);
+ for($i=0; $i<$bits; $i++) {
+ if($num & $mask) {
+ $bstream->data[$i] = 1;
+ } else {
+ $bstream->data[$i] = 0;
+ }
+ $mask = $mask >> 1;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromBytes($size, $data)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($size * 8);
+ $p=0;
+
+ for($i=0; $i<$size; $i++) {
+ $mask = 0x80;
+ for($j=0; $j<8; $j++) {
+ if($data[$i] & $mask) {
+ $bstream->data[$p] = 1;
+ } else {
+ $bstream->data[$p] = 0;
+ }
+ $p++;
+ $mask = $mask >> 1;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function append(QRbitstream $arg)
+ {
+ if (is_null($arg)) {
+ return -1;
+ }
+
+ if($arg->size() == 0) {
+ return 0;
+ }
+
+ if($this->size() == 0) {
+ $this->data = $arg->data;
+ return 0;
+ }
+
+ $this->data = array_values(array_merge($this->data, $arg->data));
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendNum($bits, $num)
+ {
+ if ($bits == 0)
+ return 0;
+
+ $b = QRbitstream::newFromNum($bits, $num);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendBytes($size, $data)
+ {
+ if ($size == 0)
+ return 0;
+
+ $b = QRbitstream::newFromBytes($size, $data);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function toByte()
+ {
+
+ $size = $this->size();
+
+ if($size == 0) {
+ return array();
+ }
+
+ $data = array_fill(0, (int)(($size + 7) / 8), 0);
+ $bytes = (int)($size / 8);
+
+ $p = 0;
+
+ for($i=0; $i<$bytes; $i++) {
+ $v = 0;
+ for($j=0; $j<8; $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$i] = $v;
+ }
+
+ if($size & 7) {
+ $v = 0;
+ for($j=0; $j<($size & 7); $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$bytes] = $v;
+ }
+
+ return $data;
+ }
+
+ }
+
+
+
+
+//---- qrsplit.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Input splitting classes
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+ class QRsplit {
+
+ public $dataStr = '';
+ public $input;
+ public $modeHint;
+
+ //----------------------------------------------------------------------
+ public function __construct($dataStr, $input, $modeHint)
+ {
+ $this->dataStr = $dataStr;
+ $this->input = $input;
+ $this->modeHint = $modeHint;
+ }
+
+ //----------------------------------------------------------------------
+ public static function isdigitat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
+ }
+
+ //----------------------------------------------------------------------
+ public static function isalnumat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return (QRinput::lookAnTable(ord($str[$pos])) >= 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function identifyMode($pos)
+ {
+ if ($pos >= strlen($this->dataStr))
+ return QR_MODE_NUL;
+
+ $c = $this->dataStr[$pos];
+
+ if(self::isdigitat($this->dataStr, $pos)) {
+ return QR_MODE_NUM;
+ } else if(self::isalnumat($this->dataStr, $pos)) {
+ return QR_MODE_AN;
+ } else if($this->modeHint == QR_MODE_KANJI) {
+
+ if ($pos+1 < strlen($this->dataStr))
+ {
+ $d = $this->dataStr[$pos+1];
+ $word = (ord($c) << 8) | ord($d);
+ if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
+ return QR_MODE_KANJI;
+ }
+ }
+ }
+
+ return QR_MODE_8;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatNum()
+ {
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+ while(self::isdigitat($this->dataStr, $p)) {
+ $p++;
+ }
+
+ $run = $p;
+ $mode = $this->identifyMode($p);
+
+ if($mode == QR_MODE_8) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ if($mode == QR_MODE_AN) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsModeAn(1) // + 4 + la
+ - QRinput::estimateBitsModeAn($run + 1);// - 4 - la
+ if($dif > 0) {
+ return $this->eatAn();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_NUM, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatAn()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+
+ while(self::isalnumat($this->dataStr, $p)) {
+ if(self::isdigitat($this->dataStr, $p)) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+
+ $dif = QRinput::estimateBitsModeAn($p) // + 4 + la
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsModeAn($q); // - 4 - la
+
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+
+ if(!self::isalnumat($this->dataStr, $p)) {
+ $dif = QRinput::estimateBitsModeAn($run) + 4 + $la
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_AN, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatKanji()
+ {
+ $p = 0;
+
+ while($this->identifyMode($p) == QR_MODE_KANJI) {
+ $p += 2;
+ }
+
+ $ret = $this->input->append(QR_MODE_KANJI, $p, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eat8()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 1;
+ $dataStrLen = strlen($this->dataStr);
+
+ while($p < $dataStrLen) {
+
+ $mode = $this->identifyMode($p);
+ if($mode == QR_MODE_KANJI) {
+ break;
+ }
+ if($mode == QR_MODE_NUM) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else if($mode == QR_MODE_AN) {
+ $q = $p;
+ while(self::isalnumat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeAn($q - $p) + 4 + $la
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+ $ret = $this->input->append(QR_MODE_8, $run, str_split($this->dataStr));
+
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function splitString()
+ {
+ while (strlen($this->dataStr) > 0)
+ {
+ if($this->dataStr == '')
+ return 0;
+
+ $mode = $this->identifyMode(0);
+
+ switch ($mode) {
+ case QR_MODE_NUM: $length = $this->eatNum(); break;
+ case QR_MODE_AN: $length = $this->eatAn(); break;
+ case QR_MODE_KANJI:
+ if ($hint == QR_MODE_KANJI)
+ $length = $this->eatKanji();
+ else $length = $this->eat8();
+ break;
+ default: $length = $this->eat8(); break;
+
+ }
+
+ if($length == 0) return 0;
+ if($length < 0) return -1;
+
+ $this->dataStr = substr($this->dataStr, $length);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function toUpper()
+ {
+ $stringLen = strlen($this->dataStr);
+ $p = 0;
+
+ while ($p<$stringLen) {
+ $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint);
+ if($mode == QR_MODE_KANJI) {
+ $p += 2;
+ } else {
+ if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) {
+ $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
+ }
+ $p++;
+ }
+ }
+
+ return $this->dataStr;
+ }
+
+ //----------------------------------------------------------------------
+ public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true)
+ {
+ if(is_null($string) || $string == '\0' || $string == '') {
+ throw new Exception('empty string!!!');
+ }
+
+ $split = new QRsplit($string, $input, $modeHint);
+
+ if(!$casesensitive)
+ $split->toUpper();
+
+ return $split->splitString();
+ }
+ }
+
+
+
+//---- qrrscode.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Reed-Solomon error correction support
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q
+ * (libfec is released under the GNU Lesser General Public License.)
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsItem {
+
+ public $mm; // Bits per symbol
+ public $nn; // Symbols per block (= (1<= $this->nn) {
+ $x -= $this->nn;
+ $x = ($x >> $this->mm) + ($x & $this->nn);
+ }
+
+ return $x;
+ }
+
+ //----------------------------------------------------------------------
+ public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ // Common code for intializing a Reed-Solomon control block (char or int symbols)
+ // Copyright 2004 Phil Karn, KA9Q
+ // May be used under the terms of the GNU Lesser General Public License (LGPL)
+
+ $rs = null;
+
+ // Check parameter ranges
+ if($symsize < 0 || $symsize > 8) return $rs;
+ if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs;
+ if($prim <= 0 || $prim >= (1<<$symsize)) return $rs;
+ if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values!
+ if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding
+
+ $rs = new QRrsItem();
+ $rs->mm = $symsize;
+ $rs->nn = (1<<$symsize)-1;
+ $rs->pad = $pad;
+
+ $rs->alpha_to = array_fill(0, $rs->nn+1, 0);
+ $rs->index_of = array_fill(0, $rs->nn+1, 0);
+
+ // PHP style macro replacement ;)
+ $NN =& $rs->nn;
+ $A0 =& $NN;
+
+ // Generate Galois field lookup tables
+ $rs->index_of[0] = $A0; // log(zero) = -inf
+ $rs->alpha_to[$A0] = 0; // alpha**-inf = 0
+ $sr = 1;
+
+ for($i=0; $i<$rs->nn; $i++) {
+ $rs->index_of[$sr] = $i;
+ $rs->alpha_to[$i] = $sr;
+ $sr <<= 1;
+ if($sr & (1<<$symsize)) {
+ $sr ^= $gfpoly;
+ }
+ $sr &= $rs->nn;
+ }
+
+ if($sr != 1){
+ // field generator polynomial is not primitive!
+ $rs = NULL;
+ return $rs;
+ }
+
+ /* Form RS code generator polynomial from its roots */
+ $rs->genpoly = array_fill(0, $nroots+1, 0);
+
+ $rs->fcr = $fcr;
+ $rs->prim = $prim;
+ $rs->nroots = $nroots;
+ $rs->gfpoly = $gfpoly;
+
+ /* Find prim-th root of 1, used in decoding */
+ for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn)
+ ; // intentional empty-body loop!
+
+ $rs->iprim = (int)($iprim / $prim);
+ $rs->genpoly[0] = 1;
+
+ for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
+ $rs->genpoly[$i+1] = 1;
+
+ // Multiply rs->genpoly[] by @**(root + x)
+ for ($j = $i; $j > 0; $j--) {
+ if ($rs->genpoly[$j] != 0) {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)];
+ } else {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1];
+ }
+ }
+ // rs->genpoly[0] can never be zero
+ $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)];
+ }
+
+ // convert rs->genpoly[] to index form for quicker encoding
+ for ($i = 0; $i <= $nroots; $i++)
+ $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]];
+
+ return $rs;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode_rs_char($data, &$parity)
+ {
+ $MM =& $this->mm;
+ $NN =& $this->nn;
+ $ALPHA_TO =& $this->alpha_to;
+ $INDEX_OF =& $this->index_of;
+ $GENPOLY =& $this->genpoly;
+ $NROOTS =& $this->nroots;
+ $FCR =& $this->fcr;
+ $PRIM =& $this->prim;
+ $IPRIM =& $this->iprim;
+ $PAD =& $this->pad;
+ $A0 =& $NN;
+
+ $parity = array_fill(0, $NROOTS, 0);
+
+ for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) {
+
+ $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
+ if($feedback != $A0) {
+ // feedback term is non-zero
+
+ // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
+ // always be for the polynomials constructed by init_rs()
+ $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback);
+
+ for($j=1;$j<$NROOTS;$j++) {
+ $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS-$j])];
+ }
+ }
+
+ // Shift
+ array_shift($parity);
+ if($feedback != $A0) {
+ array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]);
+ } else {
+ array_push($parity, 0);
+ }
+ }
+ }
+ }
+
+ //##########################################################################
+
+ class QRrs {
+
+ public static $items = array();
+
+ //----------------------------------------------------------------------
+ public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ foreach(self::$items as $rs) {
+ if($rs->pad != $pad) continue;
+ if($rs->nroots != $nroots) continue;
+ if($rs->mm != $symsize) continue;
+ if($rs->gfpoly != $gfpoly) continue;
+ if($rs->fcr != $fcr) continue;
+ if($rs->prim != $prim) continue;
+
+ return $rs;
+ }
+
+ $rs = QRrsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
+ array_unshift(self::$items, $rs);
+
+ return $rs;
+ }
+ }
+
+
+
+//---- qrmask.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Masking
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('N1', 3);
+ define('N2', 3);
+ define('N3', 40);
+ define('N4', 10);
+
+ class QRmask {
+
+ public $runLength = array();
+
+ //----------------------------------------------------------------------
+ public function __construct()
+ {
+ $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function writeFormatInformation($width, &$frame, $mask, $level)
+ {
+ $blacks = 0;
+ $format = QRspec::getFormatInfo($mask, $level);
+
+ for($i=0; $i<8; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[8][$width - 1 - $i] = chr($v);
+ if($i < 6) {
+ $frame[$i][8] = chr($v);
+ } else {
+ $frame[$i + 1][8] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+
+ for($i=0; $i<7; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[$width - 7 + $i][8] = chr($v);
+ if($i == 0) {
+ $frame[8][7] = chr($v);
+ } else {
+ $frame[8][6 - $i] = chr($v);
+ }
+
+ $format = $format >> 1;
+ }
+
+ return $blacks;
+ }
+
+ //----------------------------------------------------------------------
+ public function mask0($x, $y) { return ($x+$y)&1; }
+ public function mask1($x, $y) { return ($y&1); }
+ public function mask2($x, $y) { return ($x%3); }
+ public function mask3($x, $y) { return ($x+$y)%3; }
+ public function mask4($x, $y) { return (((int)($y/2))+((int)($x/3)))&1; }
+ public function mask5($x, $y) { return (($x*$y)&1)+($x*$y)%3; }
+ public function mask6($x, $y) { return ((($x*$y)&1)+($x*$y)%3)&1; }
+ public function mask7($x, $y) { return ((($x*$y)%3)+(($x+$y)&1))&1; }
+
+ //----------------------------------------------------------------------
+ private function generateMaskNo($maskNo, $width, $frame)
+ {
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if(ord($frame[$y][$x]) & 0x80) {
+ $bitMask[$y][$x] = 0;
+ } else {
+ $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
+ $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
+ }
+
+ }
+ }
+
+ return $bitMask;
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($bitFrame)
+ {
+ $codeArr = array();
+
+ foreach ($bitFrame as $line)
+ $codeArr[] = join('', $line);
+
+ return gzcompress(join("\n", $codeArr), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ $codeArr = array();
+
+ $codeLines = explode("\n", gzuncompress($code));
+ foreach ($codeLines as $line)
+ $codeArr[] = str_split($line);
+
+ return $codeArr;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false)
+ {
+ $b = 0;
+ $bitMask = array();
+
+ $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ $bitMask = self::unserial(file_get_contents($fileName));
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo))
+ mkdir(QR_CACHE_DIR.'mask_'.$maskNo);
+ file_put_contents($fileName, self::serial($bitMask));
+ }
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ }
+
+ if ($maskGenOnly)
+ return;
+
+ $d = $s;
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if($bitMask[$y][$x] == 1) {
+ $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
+ }
+ $b += (int)(ord($d[$y][$x]) & 1);
+ }
+ }
+
+ return $b;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMask($width, $frame, $maskNo, $level)
+ {
+ $masked = array_fill(0, $width, str_repeat("\0", $width));
+ $this->makeMaskNo($maskNo, $width, $frame, $masked);
+ $this->writeFormatInformation($width, $masked, $maskNo, $level);
+
+ return $masked;
+ }
+
+ //----------------------------------------------------------------------
+ public function calcN1N3($length)
+ {
+ $demerit = 0;
+
+ for($i=0; $i<$length; $i++) {
+
+ if($this->runLength[$i] >= 5) {
+ $demerit += (N1 + ($this->runLength[$i] - 5));
+ }
+ if($i & 1) {
+ if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) {
+ $fact = (int)($this->runLength[$i] / 3);
+ if(($this->runLength[$i-2] == $fact) &&
+ ($this->runLength[$i-1] == $fact) &&
+ ($this->runLength[$i+1] == $fact) &&
+ ($this->runLength[$i+2] == $fact)) {
+ if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) {
+ $demerit += N3;
+ } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) {
+ $demerit += N3;
+ }
+ }
+ }
+ }
+ }
+ return $demerit;
+ }
+
+ //----------------------------------------------------------------------
+ public function evaluateSymbol($width, $frame)
+ {
+ $head = 0;
+ $demerit = 0;
+
+ for($y=0; $y<$width; $y++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ $frameY = $frame[$y];
+
+ if ($y>0)
+ $frameYM = $frame[$y-1];
+
+ for($x=0; $x<$width; $x++) {
+ if(($x > 0) && ($y > 0)) {
+ $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
+ $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
+
+ if(($b22 | ($w22 ^ 1))&1) {
+ $demerit += N2;
+ }
+ }
+ if(($x == 0) && (ord($frameY[$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($x > 0) {
+ if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ for($x=0; $x<$width; $x++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ for($y=0; $y<$width; $y++) {
+ if($y == 0 && (ord($frame[$y][$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($y > 0) {
+ if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ return $demerit;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function mask($width, $frame, $level)
+ {
+ $minDemerit = PHP_INT_MAX;
+ $bestMaskNum = 0;
+ $bestMask = array();
+
+ $checked_masks = array(0,1,2,3,4,5,6,7);
+
+ if (QR_FIND_FROM_RANDOM !== false) {
+
+ $howManuOut = 8-(QR_FIND_FROM_RANDOM % 9);
+ for ($i = 0; $i < $howManuOut; $i++) {
+ $remPos = rand (0, count($checked_masks)-1);
+ unset($checked_masks[$remPos]);
+ $checked_masks = array_values($checked_masks);
+ }
+
+ }
+
+ $bestMask = $frame;
+
+ foreach($checked_masks as $i) {
+ $mask = array_fill(0, $width, str_repeat("\0", $width));
+
+ $demerit = 0;
+ $blacks = 0;
+ $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
+ $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
+ $blacks = (int)(100 * $blacks / ($width * $width));
+ $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
+ $demerit += $this->evaluateSymbol($width, $mask);
+
+ if($demerit < $minDemerit) {
+ $minDemerit = $demerit;
+ $bestMask = $mask;
+ $bestMaskNum = $i;
+ }
+ }
+
+ return $bestMask;
+ }
+
+ //----------------------------------------------------------------------
+ }
+
+
+
+
+//---- qrencode.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Main encoder classes.
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsblock {
+ public $dataLength;
+ public $data = array();
+ public $eccLength;
+ public $ecc = array();
+
+ public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs)
+ {
+ $rs->encode_rs_char($data, $ecc);
+
+ $this->dataLength = $dl;
+ $this->data = $data;
+ $this->eccLength = $el;
+ $this->ecc = $ecc;
+ }
+ };
+
+ //##########################################################################
+
+ class QRrawcode {
+ public $version;
+ public $datacode = array();
+ public $ecccode = array();
+ public $blocks;
+ public $rsblocks = array(); //of RSblock
+ public $count;
+ public $dataLength;
+ public $eccLength;
+ public $b1;
+
+ //----------------------------------------------------------------------
+ public function __construct(QRinput $input)
+ {
+ $spec = array(0,0,0,0,0);
+
+ $this->datacode = $input->getByteStream();
+ if(is_null($this->datacode)) {
+ throw new Exception('null imput string');
+ }
+
+ QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec);
+
+ $this->version = $input->getVersion();
+ $this->b1 = QRspec::rsBlockNum1($spec);
+ $this->dataLength = QRspec::rsDataLength($spec);
+ $this->eccLength = QRspec::rsEccLength($spec);
+ $this->ecccode = array_fill(0, $this->eccLength, 0);
+ $this->blocks = QRspec::rsBlockNum($spec);
+
+ $ret = $this->init($spec);
+ if($ret < 0) {
+ throw new Exception('block alloc error');
+ return null;
+ }
+
+ $this->count = 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function init(array $spec)
+ {
+ $dl = QRspec::rsDataCodes1($spec);
+ $el = QRspec::rsEccCodes1($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+
+ $blockNo = 0;
+ $dataPos = 0;
+ $eccPos = 0;
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ if(QRspec::rsBlockNum2($spec) == 0)
+ return 0;
+
+ $dl = QRspec::rsDataCodes2($spec);
+ $el = QRspec::rsEccCodes2($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+ if($rs == NULL) return -1;
+
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getCode()
+ {
+ $ret;
+
+ if($this->count < $this->dataLength) {
+ $row = $this->count % $this->blocks;
+ $col = $this->count / $this->blocks;
+ if($col >= $this->rsblocks[0]->dataLength) {
+ $row += $this->b1;
+ }
+ $ret = $this->rsblocks[$row]->data[$col];
+ } else if($this->count < $this->dataLength + $this->eccLength) {
+ $row = ($this->count - $this->dataLength) % $this->blocks;
+ $col = ($this->count - $this->dataLength) / $this->blocks;
+ $ret = $this->rsblocks[$row]->ecc[$col];
+ } else {
+ return 0;
+ }
+ $this->count++;
+
+ return $ret;
+ }
+ }
+
+ //##########################################################################
+
+ class QRcode {
+
+ public $version;
+ public $width;
+ public $data;
+
+ //----------------------------------------------------------------------
+ public function encodeMask(QRinput $input, $mask)
+ {
+ if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) {
+ throw new Exception('wrong version');
+ }
+ if($input->getErrorCorrectionLevel() > QR_ECLEVEL_H) {
+ throw new Exception('wrong level');
+ }
+
+ $raw = new QRrawcode($input);
+
+ QRtools::markTime('after_raw');
+
+ $version = $raw->version;
+ $width = QRspec::getWidth($version);
+ $frame = QRspec::newFrame($version);
+
+ $filler = new FrameFiller($width, $frame);
+ if(is_null($filler)) {
+ return NULL;
+ }
+
+ // inteleaved data and ecc codes
+ for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) {
+ $code = $raw->getCode();
+ $bit = 0x80;
+ for($j=0; $j<8; $j++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
+ $bit = $bit >> 1;
+ }
+ }
+
+ QRtools::markTime('after_filler');
+
+ unset($raw);
+
+ // remainder bits
+ $j = QRspec::getRemainder($version);
+ for($i=0; $i<$j; $i++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02);
+ }
+
+ $frame = $filler->frame;
+ unset($filler);
+
+
+ // masking
+ $maskObj = new QRmask();
+ if($mask < 0) {
+
+ if (QR_FIND_BEST_MASK) {
+ $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, (intval(QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel());
+ }
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());
+ }
+
+ if($masked == NULL) {
+ return NULL;
+ }
+
+ QRtools::markTime('after_mask');
+
+ $this->version = $version;
+ $this->width = $width;
+ $this->data = $masked;
+
+ return $this;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeInput(QRinput $input)
+ {
+ return $this->encodeMask($input, -1);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString8bit($string, $version, $level)
+ {
+ if(string == NULL) {
+ throw new Exception('empty string!');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = $input->append($input, QR_MODE_8, strlen($string), str_split($string));
+ if($ret < 0) {
+ unset($input);
+ return NULL;
+ }
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString($string, $version, $level, $hint, $casesensitive)
+ {
+
+ if($hint != QR_MODE_8 && $hint != QR_MODE_KANJI) {
+ throw new Exception('bad hint');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive);
+ if($ret < 0) {
+ return NULL;
+ }
+
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public static function png($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodePNG($text, $outfile, $saveandprint=false);
+ }
+
+ //----------------------------------------------------------------------
+ public static function text($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encode($text, $outfile);
+ }
+
+ //----------------------------------------------------------------------
+ public static function raw($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodeRAW($text, $outfile);
+ }
+ }
+
+ //##########################################################################
+
+ class FrameFiller {
+
+ public $width;
+ public $frame;
+ public $x;
+ public $y;
+ public $dir;
+ public $bit;
+
+ //----------------------------------------------------------------------
+ public function __construct($width, &$frame)
+ {
+ $this->width = $width;
+ $this->frame = $frame;
+ $this->x = $width - 1;
+ $this->y = $width - 1;
+ $this->dir = -1;
+ $this->bit = -1;
+ }
+
+ //----------------------------------------------------------------------
+ public function setFrameAt($at, $val)
+ {
+ $this->frame[$at['y']][$at['x']] = chr($val);
+ }
+
+ //----------------------------------------------------------------------
+ public function getFrameAt($at)
+ {
+ return ord($this->frame[$at['y']][$at['x']]);
+ }
+
+ //----------------------------------------------------------------------
+ public function next()
+ {
+ do {
+
+ if($this->bit == -1) {
+ $this->bit = 0;
+ return array('x'=>$this->x, 'y'=>$this->y);
+ }
+
+ $x = $this->x;
+ $y = $this->y;
+ $w = $this->width;
+
+ if($this->bit == 0) {
+ $x--;
+ $this->bit++;
+ } else {
+ $x++;
+ $y += $this->dir;
+ $this->bit--;
+ }
+
+ if($this->dir < 0) {
+ if($y < 0) {
+ $y = 0;
+ $x -= 2;
+ $this->dir = 1;
+ if($x == 6) {
+ $x--;
+ $y = 9;
+ }
+ }
+ } else {
+ if($y == $w) {
+ $y = $w - 1;
+ $x -= 2;
+ $this->dir = -1;
+ if($x == 6) {
+ $x--;
+ $y -= 8;
+ }
+ }
+ }
+ if($x < 0 || $y < 0) return null;
+
+ $this->x = $x;
+ $this->y = $y;
+
+ } while(ord($this->frame[$y][$x]) & 0x80);
+
+ return array('x'=>$x, 'y'=>$y);
+ }
+
+ } ;
+
+ //##########################################################################
+
+ class QRencode {
+
+ public $casesensitive = true;
+ public $eightbit = false;
+
+ public $version = 0;
+ public $size = 3;
+ public $margin = 4;
+
+ public $structured = 0; // not supported yet
+
+ public $level = QR_ECLEVEL_L;
+ public $hint = QR_MODE_8;
+
+ //----------------------------------------------------------------------
+ public static function factory($level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = new QRencode();
+ $enc->size = $size;
+ $enc->margin = $margin;
+
+ switch ($level.'') {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ $enc->level = $level;
+ break;
+ case 'l':
+ case 'L':
+ $enc->level = QR_ECLEVEL_L;
+ break;
+ case 'm':
+ case 'M':
+ $enc->level = QR_ECLEVEL_M;
+ break;
+ case 'q':
+ case 'Q':
+ $enc->level = QR_ECLEVEL_Q;
+ break;
+ case 'h':
+ case 'H':
+ $enc->level = QR_ECLEVEL_H;
+ break;
+ }
+
+ return $enc;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeRAW($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ return $code->data;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ QRtools::markTime('after_encode');
+
+ if ($outfile!== false) {
+ file_put_contents($outfile, join("\n", QRtools::binarize($code->data)));
+ } else {
+ return QRtools::binarize($code->data);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodePNG($intext, $outfile = false,$saveandprint=false)
+ {
+ try {
+
+ ob_start();
+ $tab = $this->encode($intext);
+ $err = ob_get_contents();
+ ob_end_clean();
+
+ if ($err != '')
+ QRtools::log($outfile, $err);
+
+ $maxSize = (int)(QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin));
+
+ QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint);
+
+ } catch (Exception $e) {
+
+ QRtools::log($outfile, $e->getMessage());
+
+ }
+ }
+ }
+
+
diff --git a/app/lib/phpqrcode/qrbitstream.php b/app/lib/phpqrcode/qrbitstream.php
new file mode 100644
index 00000000..7d4ec4a6
--- /dev/null
+++ b/app/lib/phpqrcode/qrbitstream.php
@@ -0,0 +1,180 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRbitstream {
+
+ public $data = array();
+
+ //----------------------------------------------------------------------
+ public function size()
+ {
+ return count($this->data);
+ }
+
+ //----------------------------------------------------------------------
+ public function allocate($setLength)
+ {
+ $this->data = array_fill(0, $setLength, 0);
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromNum($bits, $num)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($bits);
+
+ $mask = 1 << ($bits - 1);
+ for($i=0; $i<$bits; $i++) {
+ if($num & $mask) {
+ $bstream->data[$i] = 1;
+ } else {
+ $bstream->data[$i] = 0;
+ }
+ $mask = $mask >> 1;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromBytes($size, $data)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($size * 8);
+ $p=0;
+
+ for($i=0; $i<$size; $i++) {
+ $mask = 0x80;
+ for($j=0; $j<8; $j++) {
+ if($data[$i] & $mask) {
+ $bstream->data[$p] = 1;
+ } else {
+ $bstream->data[$p] = 0;
+ }
+ $p++;
+ $mask = $mask >> 1;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function append(QRbitstream $arg)
+ {
+ if (is_null($arg)) {
+ return -1;
+ }
+
+ if($arg->size() == 0) {
+ return 0;
+ }
+
+ if($this->size() == 0) {
+ $this->data = $arg->data;
+ return 0;
+ }
+
+ $this->data = array_values(array_merge($this->data, $arg->data));
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendNum($bits, $num)
+ {
+ if ($bits == 0)
+ return 0;
+
+ $b = QRbitstream::newFromNum($bits, $num);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendBytes($size, $data)
+ {
+ if ($size == 0)
+ return 0;
+
+ $b = QRbitstream::newFromBytes($size, $data);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function toByte()
+ {
+
+ $size = $this->size();
+
+ if($size == 0) {
+ return array();
+ }
+
+ $data = array_fill(0, (int)(($size + 7) / 8), 0);
+ $bytes = (int)($size / 8);
+
+ $p = 0;
+
+ for($i=0; $i<$bytes; $i++) {
+ $v = 0;
+ for($j=0; $j<8; $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$i] = $v;
+ }
+
+ if($size & 7) {
+ $v = 0;
+ for($j=0; $j<($size & 7); $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$bytes] = $v;
+ }
+
+ return $data;
+ }
+
+ }
diff --git a/app/lib/phpqrcode/qrconfig.php b/app/lib/phpqrcode/qrconfig.php
new file mode 100644
index 00000000..e53dff8c
--- /dev/null
+++ b/app/lib/phpqrcode/qrconfig.php
@@ -0,0 +1,17 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ // Encoding modes
+
+ define('QR_MODE_NUL', -1);
+ define('QR_MODE_NUM', 0);
+ define('QR_MODE_AN', 1);
+ define('QR_MODE_8', 2);
+ define('QR_MODE_KANJI', 3);
+ define('QR_MODE_STRUCTURE', 4);
+
+ // Levels of error correction.
+
+ define('QR_ECLEVEL_L', 0);
+ define('QR_ECLEVEL_M', 1);
+ define('QR_ECLEVEL_Q', 2);
+ define('QR_ECLEVEL_H', 3);
+
+ // Supported output formats
+
+ define('QR_FORMAT_TEXT', 0);
+ define('QR_FORMAT_PNG', 1);
+
+ class qrstr {
+ public static function set(&$srctab, $x, $y, $repl, $replLen = false) {
+ $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
+ }
+ }
\ No newline at end of file
diff --git a/app/lib/phpqrcode/qrencode.php b/app/lib/phpqrcode/qrencode.php
new file mode 100644
index 00000000..4b77a5bd
--- /dev/null
+++ b/app/lib/phpqrcode/qrencode.php
@@ -0,0 +1,502 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsblock {
+ public $dataLength;
+ public $data = array();
+ public $eccLength;
+ public $ecc = array();
+
+ public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs)
+ {
+ $rs->encode_rs_char($data, $ecc);
+
+ $this->dataLength = $dl;
+ $this->data = $data;
+ $this->eccLength = $el;
+ $this->ecc = $ecc;
+ }
+ };
+
+ //##########################################################################
+
+ class QRrawcode {
+ public $version;
+ public $datacode = array();
+ public $ecccode = array();
+ public $blocks;
+ public $rsblocks = array(); //of RSblock
+ public $count;
+ public $dataLength;
+ public $eccLength;
+ public $b1;
+
+ //----------------------------------------------------------------------
+ public function __construct(QRinput $input)
+ {
+ $spec = array(0,0,0,0,0);
+
+ $this->datacode = $input->getByteStream();
+ if(is_null($this->datacode)) {
+ throw new Exception('null imput string');
+ }
+
+ QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec);
+
+ $this->version = $input->getVersion();
+ $this->b1 = QRspec::rsBlockNum1($spec);
+ $this->dataLength = QRspec::rsDataLength($spec);
+ $this->eccLength = QRspec::rsEccLength($spec);
+ $this->ecccode = array_fill(0, $this->eccLength, 0);
+ $this->blocks = QRspec::rsBlockNum($spec);
+
+ $ret = $this->init($spec);
+ if($ret < 0) {
+ throw new Exception('block alloc error');
+ return null;
+ }
+
+ $this->count = 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function init(array $spec)
+ {
+ $dl = QRspec::rsDataCodes1($spec);
+ $el = QRspec::rsEccCodes1($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+
+ $blockNo = 0;
+ $dataPos = 0;
+ $eccPos = 0;
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ if(QRspec::rsBlockNum2($spec) == 0)
+ return 0;
+
+ $dl = QRspec::rsDataCodes2($spec);
+ $el = QRspec::rsEccCodes2($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+ if($rs == NULL) return -1;
+
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getCode()
+ {
+ $ret;
+
+ if($this->count < $this->dataLength) {
+ $row = $this->count % $this->blocks;
+ $col = $this->count / $this->blocks;
+ if($col >= $this->rsblocks[0]->dataLength) {
+ $row += $this->b1;
+ }
+ $ret = $this->rsblocks[$row]->data[$col];
+ } else if($this->count < $this->dataLength + $this->eccLength) {
+ $row = ($this->count - $this->dataLength) % $this->blocks;
+ $col = ($this->count - $this->dataLength) / $this->blocks;
+ $ret = $this->rsblocks[$row]->ecc[$col];
+ } else {
+ return 0;
+ }
+ $this->count++;
+
+ return $ret;
+ }
+ }
+
+ //##########################################################################
+
+ class QRcode {
+
+ public $version;
+ public $width;
+ public $data;
+
+ //----------------------------------------------------------------------
+ public function encodeMask(QRinput $input, $mask)
+ {
+ if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) {
+ throw new Exception('wrong version');
+ }
+ if($input->getErrorCorrectionLevel() > QR_ECLEVEL_H) {
+ throw new Exception('wrong level');
+ }
+
+ $raw = new QRrawcode($input);
+
+ QRtools::markTime('after_raw');
+
+ $version = $raw->version;
+ $width = QRspec::getWidth($version);
+ $frame = QRspec::newFrame($version);
+
+ $filler = new FrameFiller($width, $frame);
+ if(is_null($filler)) {
+ return NULL;
+ }
+
+ // inteleaved data and ecc codes
+ for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) {
+ $code = $raw->getCode();
+ $bit = 0x80;
+ for($j=0; $j<8; $j++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
+ $bit = $bit >> 1;
+ }
+ }
+
+ QRtools::markTime('after_filler');
+
+ unset($raw);
+
+ // remainder bits
+ $j = QRspec::getRemainder($version);
+ for($i=0; $i<$j; $i++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02);
+ }
+
+ $frame = $filler->frame;
+ unset($filler);
+
+
+ // masking
+ $maskObj = new QRmask();
+ if($mask < 0) {
+
+ if (QR_FIND_BEST_MASK) {
+ $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, (intval(QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel());
+ }
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());
+ }
+
+ if($masked == NULL) {
+ return NULL;
+ }
+
+ QRtools::markTime('after_mask');
+
+ $this->version = $version;
+ $this->width = $width;
+ $this->data = $masked;
+
+ return $this;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeInput(QRinput $input)
+ {
+ return $this->encodeMask($input, -1);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString8bit($string, $version, $level)
+ {
+ if(string == NULL) {
+ throw new Exception('empty string!');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = $input->append($input, QR_MODE_8, strlen($string), str_split($string));
+ if($ret < 0) {
+ unset($input);
+ return NULL;
+ }
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString($string, $version, $level, $hint, $casesensitive)
+ {
+
+ if($hint != QR_MODE_8 && $hint != QR_MODE_KANJI) {
+ throw new Exception('bad hint');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive);
+ if($ret < 0) {
+ return NULL;
+ }
+
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public static function png($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodePNG($text, $outfile, $saveandprint=false);
+ }
+
+ //----------------------------------------------------------------------
+ public static function text($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encode($text, $outfile);
+ }
+
+ //----------------------------------------------------------------------
+ public static function raw($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodeRAW($text, $outfile);
+ }
+ }
+
+ //##########################################################################
+
+ class FrameFiller {
+
+ public $width;
+ public $frame;
+ public $x;
+ public $y;
+ public $dir;
+ public $bit;
+
+ //----------------------------------------------------------------------
+ public function __construct($width, &$frame)
+ {
+ $this->width = $width;
+ $this->frame = $frame;
+ $this->x = $width - 1;
+ $this->y = $width - 1;
+ $this->dir = -1;
+ $this->bit = -1;
+ }
+
+ //----------------------------------------------------------------------
+ public function setFrameAt($at, $val)
+ {
+ $this->frame[$at['y']][$at['x']] = chr($val);
+ }
+
+ //----------------------------------------------------------------------
+ public function getFrameAt($at)
+ {
+ return ord($this->frame[$at['y']][$at['x']]);
+ }
+
+ //----------------------------------------------------------------------
+ public function next()
+ {
+ do {
+
+ if($this->bit == -1) {
+ $this->bit = 0;
+ return array('x'=>$this->x, 'y'=>$this->y);
+ }
+
+ $x = $this->x;
+ $y = $this->y;
+ $w = $this->width;
+
+ if($this->bit == 0) {
+ $x--;
+ $this->bit++;
+ } else {
+ $x++;
+ $y += $this->dir;
+ $this->bit--;
+ }
+
+ if($this->dir < 0) {
+ if($y < 0) {
+ $y = 0;
+ $x -= 2;
+ $this->dir = 1;
+ if($x == 6) {
+ $x--;
+ $y = 9;
+ }
+ }
+ } else {
+ if($y == $w) {
+ $y = $w - 1;
+ $x -= 2;
+ $this->dir = -1;
+ if($x == 6) {
+ $x--;
+ $y -= 8;
+ }
+ }
+ }
+ if($x < 0 || $y < 0) return null;
+
+ $this->x = $x;
+ $this->y = $y;
+
+ } while(ord($this->frame[$y][$x]) & 0x80);
+
+ return array('x'=>$x, 'y'=>$y);
+ }
+
+ } ;
+
+ //##########################################################################
+
+ class QRencode {
+
+ public $casesensitive = true;
+ public $eightbit = false;
+
+ public $version = 0;
+ public $size = 3;
+ public $margin = 4;
+
+ public $structured = 0; // not supported yet
+
+ public $level = QR_ECLEVEL_L;
+ public $hint = QR_MODE_8;
+
+ //----------------------------------------------------------------------
+ public static function factory($level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = new QRencode();
+ $enc->size = $size;
+ $enc->margin = $margin;
+
+ switch ($level.'') {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ $enc->level = $level;
+ break;
+ case 'l':
+ case 'L':
+ $enc->level = QR_ECLEVEL_L;
+ break;
+ case 'm':
+ case 'M':
+ $enc->level = QR_ECLEVEL_M;
+ break;
+ case 'q':
+ case 'Q':
+ $enc->level = QR_ECLEVEL_Q;
+ break;
+ case 'h':
+ case 'H':
+ $enc->level = QR_ECLEVEL_H;
+ break;
+ }
+
+ return $enc;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeRAW($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ return $code->data;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ QRtools::markTime('after_encode');
+
+ if ($outfile!== false) {
+ file_put_contents($outfile, join("\n", QRtools::binarize($code->data)));
+ } else {
+ return QRtools::binarize($code->data);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodePNG($intext, $outfile = false,$saveandprint=false)
+ {
+ try {
+
+ ob_start();
+ $tab = $this->encode($intext);
+ $err = ob_get_contents();
+ ob_end_clean();
+
+ if ($err != '')
+ QRtools::log($outfile, $err);
+
+ $maxSize = (int)(QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin));
+
+ QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint);
+
+ } catch (Exception $e) {
+
+ QRtools::log($outfile, $e->getMessage());
+
+ }
+ }
+ }
diff --git a/app/lib/phpqrcode/qrimage.php b/app/lib/phpqrcode/qrimage.php
new file mode 100644
index 00000000..10b0a6e1
--- /dev/null
+++ b/app/lib/phpqrcode/qrimage.php
@@ -0,0 +1,95 @@
+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('QR_IMAGE', true);
+
+ class QRimage {
+
+ //----------------------------------------------------------------------
+ public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/png");
+ ImagePng($image);
+ } else {
+ if($saveandprint===TRUE){
+ ImagePng($image, $filename);
+ header("Content-type: image/png");
+ ImagePng($image);
+ }else{
+ ImagePng($image, $filename);
+ }
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/jpeg");
+ ImageJpeg($image, null, $q);
+ } else {
+ ImageJpeg($image, $filename, $q);
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ private static function image($frame, $pixelPerPoint = 4, $outerFrame = 4)
+ {
+ $h = count($frame);
+ $w = strlen($frame[0]);
+
+ $imgW = $w + 2*$outerFrame;
+ $imgH = $h + 2*$outerFrame;
+
+ $base_image =ImageCreate($imgW, $imgH);
+
+ $col[0] = ImageColorAllocate($base_image,255,255,255);
+ $col[1] = ImageColorAllocate($base_image,0,0,0);
+
+ imagefill($base_image, 0, 0, $col[0]);
+
+ for($y=0; $y<$h; $y++) {
+ for($x=0; $x<$w; $x++) {
+ if ($frame[$y][$x] == '1') {
+ ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]);
+ }
+ }
+ }
+
+ $target_image =ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint);
+ ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH);
+ ImageDestroy($base_image);
+
+ return $target_image;
+ }
+ }
\ No newline at end of file
diff --git a/app/lib/phpqrcode/qrinput.php b/app/lib/phpqrcode/qrinput.php
new file mode 100644
index 00000000..0f6d7f94
--- /dev/null
+++ b/app/lib/phpqrcode/qrinput.php
@@ -0,0 +1,729 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('STRUCTURE_HEADER_BITS', 20);
+ define('MAX_STRUCTURED_SYMBOLS', 16);
+
+ class QRinputItem {
+
+ public $mode;
+ public $size;
+ public $data;
+ public $bstream;
+
+ public function __construct($mode, $size, $data, $bstream = null)
+ {
+ $setData = array_slice($data, 0, $size);
+
+ if (count($setData) < $size) {
+ $setData = array_merge($setData, array_fill(0,$size-count($setData),0));
+ }
+
+ if(!QRinput::check($mode, $size, $setData)) {
+ throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData));
+ return null;
+ }
+
+ $this->mode = $mode;
+ $this->size = $size;
+ $this->data = $setData;
+ $this->bstream = $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeNum($version)
+ {
+ try {
+
+ $words = (int)($this->size / 3);
+ $bs = new QRbitstream();
+
+ $val = 0x1;
+ $bs->appendNum(4, $val);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (ord($this->data[$i*3 ]) - ord('0')) * 100;
+ $val += (ord($this->data[$i*3+1]) - ord('0')) * 10;
+ $val += (ord($this->data[$i*3+2]) - ord('0'));
+ $bs->appendNum(10, $val);
+ }
+
+ if($this->size - $words * 3 == 1) {
+ $val = ord($this->data[$words*3]) - ord('0');
+ $bs->appendNum(4, $val);
+ } else if($this->size - $words * 3 == 2) {
+ $val = (ord($this->data[$words*3 ]) - ord('0')) * 10;
+ $val += (ord($this->data[$words*3+1]) - ord('0'));
+ $bs->appendNum(7, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeAn($version)
+ {
+ try {
+ $words = (int)($this->size / 2);
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x02);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45;
+ $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1]));
+
+ $bs->appendNum(11, $val);
+ }
+
+ if($this->size & 1) {
+ $val = QRinput::lookAnTable(ord($this->data[$words * 2]));
+ $bs->appendNum(6, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeMode8($version)
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x4);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size);
+
+ for($i=0; $i<$this->size; $i++) {
+ $bs->appendNum(8, ord($this->data[$i]));
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeKanji($version)
+ {
+ try {
+
+ $bs = new QRbitrtream();
+
+ $bs->appendNum(4, 0x8);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2));
+
+ for($i=0; $i<$this->size; $i+=2) {
+ $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);
+ if($val <= 0x9ffc) {
+ $val -= 0x8140;
+ } else {
+ $val -= 0xc140;
+ }
+
+ $h = ($val >> 8) * 0xc0;
+ $val = ($val & 0xff) + $h;
+
+ $bs->appendNum(13, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeStructure()
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x03);
+ $bs->appendNum(4, ord($this->data[1]) - 1);
+ $bs->appendNum(4, ord($this->data[0]) - 1);
+ $bs->appendNum(8, ord($this->data[2]));
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSizeOfEntry($version)
+ {
+ $bits = 0;
+
+ if($version == 0)
+ $version = 1;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break;
+ case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break;
+ case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break;
+ case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break;
+ case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS;
+ default:
+ return 0;
+ }
+
+ $l = QRspec::lengthIndicator($this->mode, $version);
+ $m = 1 << $l;
+ $num = (int)(($this->size + $m - 1) / $m);
+
+ $bits += $num * (4 + $l);
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeBitStream($version)
+ {
+ try {
+
+ unset($this->bstream);
+ $words = QRspec::maximumWords($this->mode, $version);
+
+ if($this->size > $words) {
+
+ $st1 = new QRinputItem($this->mode, $words, $this->data);
+ $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words));
+
+ $st1->encodeBitStream($version);
+ $st2->encodeBitStream($version);
+
+ $this->bstream = new QRbitstream();
+ $this->bstream->append($st1->bstream);
+ $this->bstream->append($st2->bstream);
+
+ unset($st1);
+ unset($st2);
+
+ } else {
+
+ $ret = 0;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break;
+ case QR_MODE_AN: $ret = $this->encodeModeAn($version); break;
+ case QR_MODE_8: $ret = $this->encodeMode8($version); break;
+ case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break;
+ case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break;
+
+ default:
+ break;
+ }
+
+ if($ret < 0)
+ return -1;
+ }
+
+ return $this->bstream->size();
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+ };
+
+ //##########################################################################
+
+ class QRinput {
+
+ public $items;
+
+ private $version;
+ private $level;
+
+ //----------------------------------------------------------------------
+ public function __construct($version = 0, $level = QR_ECLEVEL_L)
+ {
+ if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid version no');
+ return NULL;
+ }
+
+ $this->version = $version;
+ $this->level = $level;
+ }
+
+ //----------------------------------------------------------------------
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ //----------------------------------------------------------------------
+ public function setVersion($version)
+ {
+ if($version < 0 || $version > QRSPEC_VERSION_MAX) {
+ throw new Exception('Invalid version no');
+ return -1;
+ }
+
+ $this->version = $version;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getErrorCorrectionLevel()
+ {
+ return $this->level;
+ }
+
+ //----------------------------------------------------------------------
+ public function setErrorCorrectionLevel($level)
+ {
+ if($level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid ECLEVEL');
+ return -1;
+ }
+
+ $this->level = $level;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendEntry(QRinputItem $entry)
+ {
+ $this->items[] = $entry;
+ }
+
+ //----------------------------------------------------------------------
+ public function append($mode, $size, $data)
+ {
+ try {
+ $entry = new QRinputItem($mode, $size, $data);
+ $this->items[] = $entry;
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+
+ public function insertStructuredAppendHeader($size, $index, $parity)
+ {
+ if( $size > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong size');
+ }
+
+ if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong index');
+ }
+
+ $buf = array($size, $index, $parity);
+
+ try {
+ $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf);
+ array_unshift($this->items, $entry);
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function calcParity()
+ {
+ $parity = 0;
+
+ foreach($this->items as $item) {
+ if($item->mode != QR_MODE_STRUCTURE) {
+ for($i=$item->size-1; $i>=0; $i--) {
+ $parity ^= $item->data[$i];
+ }
+ }
+ }
+
+ return $parity;
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeNum($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeNum($size)
+ {
+ $w = (int)$size / 3;
+ $bits = $w * 10;
+
+ switch($size - $w * 3) {
+ case 1:
+ $bits += 4;
+ break;
+ case 2:
+ $bits += 7;
+ break;
+ default:
+ break;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static $anTable = array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ );
+
+ //----------------------------------------------------------------------
+ public static function lookAnTable($c)
+ {
+ return (($c > 127)?-1:self::$anTable[$c]);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeAn($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if (self::lookAnTable(ord($data[$i])) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeAn($size)
+ {
+ $w = (int)($size / 2);
+ $bits = $w * 11;
+
+ if($size & 1) {
+ $bits += 6;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsMode8($size)
+ {
+ return $size * 8;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitsModeKanji($size)
+ {
+ return (int)(($size / 2) * 13);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeKanji($size, $data)
+ {
+ if($size & 1)
+ return false;
+
+ for($i=0; $i<$size; $i+=2) {
+ $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
+ if( $val < 0x8140
+ || ($val > 0x9ffc && $val < 0xe040)
+ || $val > 0xebbf) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /***********************************************************************
+ * Validation
+ **********************************************************************/
+
+ public static function check($mode, $size, $data)
+ {
+ if($size <= 0)
+ return false;
+
+ switch($mode) {
+ case QR_MODE_NUM: return self::checkModeNum($size, $data); break;
+ case QR_MODE_AN: return self::checkModeAn($size, $data); break;
+ case QR_MODE_KANJI: return self::checkModeKanji($size, $data); break;
+ case QR_MODE_8: return true; break;
+ case QR_MODE_STRUCTURE: return true; break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSize($version)
+ {
+ $bits = 0;
+
+ foreach($this->items as $item) {
+ $bits += $item->estimateBitStreamSizeOfEntry($version);
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateVersion()
+ {
+ $version = 0;
+ $prev = 0;
+ do {
+ $prev = $version;
+ $bits = $this->estimateBitStreamSize($prev);
+ $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($version < 0) {
+ return -1;
+ }
+ } while ($version > $prev);
+
+ return $version;
+ }
+
+ //----------------------------------------------------------------------
+ public static function lengthOfCode($mode, $version, $bits)
+ {
+ $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);
+ switch($mode) {
+ case QR_MODE_NUM:
+ $chunks = (int)($payload / 10);
+ $remain = $payload - $chunks * 10;
+ $size = $chunks * 3;
+ if($remain >= 7) {
+ $size += 2;
+ } else if($remain >= 4) {
+ $size += 1;
+ }
+ break;
+ case QR_MODE_AN:
+ $chunks = (int)($payload / 11);
+ $remain = $payload - $chunks * 11;
+ $size = $chunks * 2;
+ if($remain >= 6)
+ $size++;
+ break;
+ case QR_MODE_8:
+ $size = (int)($payload / 8);
+ break;
+ case QR_MODE_KANJI:
+ $size = (int)(($payload / 13) * 2);
+ break;
+ case QR_MODE_STRUCTURE:
+ $size = (int)($payload / 8);
+ break;
+ default:
+ $size = 0;
+ break;
+ }
+
+ $maxsize = QRspec::maximumWords($mode, $version);
+ if($size < 0) $size = 0;
+ if($size > $maxsize) $size = $maxsize;
+
+ return $size;
+ }
+
+ //----------------------------------------------------------------------
+ public function createBitStream()
+ {
+ $total = 0;
+
+ foreach($this->items as $item) {
+ $bits = $item->encodeBitStream($this->version);
+
+ if($bits < 0)
+ return -1;
+
+ $total += $bits;
+ }
+
+ return $total;
+ }
+
+ //----------------------------------------------------------------------
+ public function convertData()
+ {
+ $ver = $this->estimateVersion();
+ if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ }
+
+ for(;;) {
+ $bits = $this->createBitStream();
+
+ if($bits < 0)
+ return -1;
+
+ $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if($ver < 0) {
+ throw new Exception('WRONG VERSION');
+ return -1;
+ } else if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ } else {
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendPaddingBit(&$bstream)
+ {
+ $bits = $bstream->size();
+ $maxwords = QRspec::getDataLength($this->version, $this->level);
+ $maxbits = $maxwords * 8;
+
+ if ($maxbits == $bits) {
+ return 0;
+ }
+
+ if ($maxbits - $bits < 5) {
+ return $bstream->appendNum($maxbits - $bits, 0);
+ }
+
+ $bits += 4;
+ $words = (int)(($bits + 7) / 8);
+
+ $padding = new QRbitstream();
+ $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
+
+ if($ret < 0)
+ return $ret;
+
+ $padlen = $maxwords - $words;
+
+ if($padlen > 0) {
+
+ $padbuf = array();
+ for($i=0; $i<$padlen; $i++) {
+ $padbuf[$i] = ($i&1)?0x11:0xec;
+ }
+
+ $ret = $padding->appendBytes($padlen, $padbuf);
+
+ if($ret < 0)
+ return $ret;
+
+ }
+
+ $ret = $bstream->append($padding);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function mergeBitStream()
+ {
+ if($this->convertData() < 0) {
+ return null;
+ }
+
+ $bstream = new QRbitstream();
+
+ foreach($this->items as $item) {
+ $ret = $bstream->append($item->bstream);
+ if($ret < 0) {
+ return null;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getBitStream()
+ {
+
+ $bstream = $this->mergeBitStream();
+
+ if($bstream == null) {
+ return null;
+ }
+
+ $ret = $this->appendPaddingBit($bstream);
+ if($ret < 0) {
+ return null;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getByteStream()
+ {
+ $bstream = $this->getBitStream();
+ if($bstream == null) {
+ return null;
+ }
+
+ return $bstream->toByte();
+ }
+ }
+
+
+
\ No newline at end of file
diff --git a/app/lib/phpqrcode/qrlib.php b/app/lib/phpqrcode/qrlib.php
new file mode 100644
index 00000000..d55c4af2
--- /dev/null
+++ b/app/lib/phpqrcode/qrlib.php
@@ -0,0 +1,43 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ $QR_BASEDIR = dirname(__FILE__).DIRECTORY_SEPARATOR;
+
+ // Required libs
+
+ include $QR_BASEDIR."qrconst.php";
+ include $QR_BASEDIR."qrconfig.php";
+ include $QR_BASEDIR."qrtools.php";
+ include $QR_BASEDIR."qrspec.php";
+ include $QR_BASEDIR."qrimage.php";
+ include $QR_BASEDIR."qrinput.php";
+ include $QR_BASEDIR."qrbitstream.php";
+ include $QR_BASEDIR."qrsplit.php";
+ include $QR_BASEDIR."qrrscode.php";
+ include $QR_BASEDIR."qrmask.php";
+ include $QR_BASEDIR."qrencode.php";
+
diff --git a/app/lib/phpqrcode/qrmask.php b/app/lib/phpqrcode/qrmask.php
new file mode 100644
index 00000000..b14d7ae1
--- /dev/null
+++ b/app/lib/phpqrcode/qrmask.php
@@ -0,0 +1,328 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('N1', 3);
+ define('N2', 3);
+ define('N3', 40);
+ define('N4', 10);
+
+ class QRmask {
+
+ public $runLength = array();
+
+ //----------------------------------------------------------------------
+ public function __construct()
+ {
+ $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function writeFormatInformation($width, &$frame, $mask, $level)
+ {
+ $blacks = 0;
+ $format = QRspec::getFormatInfo($mask, $level);
+
+ for($i=0; $i<8; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[8][$width - 1 - $i] = chr($v);
+ if($i < 6) {
+ $frame[$i][8] = chr($v);
+ } else {
+ $frame[$i + 1][8] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+
+ for($i=0; $i<7; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[$width - 7 + $i][8] = chr($v);
+ if($i == 0) {
+ $frame[8][7] = chr($v);
+ } else {
+ $frame[8][6 - $i] = chr($v);
+ }
+
+ $format = $format >> 1;
+ }
+
+ return $blacks;
+ }
+
+ //----------------------------------------------------------------------
+ public function mask0($x, $y) { return ($x+$y)&1; }
+ public function mask1($x, $y) { return ($y&1); }
+ public function mask2($x, $y) { return ($x%3); }
+ public function mask3($x, $y) { return ($x+$y)%3; }
+ public function mask4($x, $y) { return (((int)($y/2))+((int)($x/3)))&1; }
+ public function mask5($x, $y) { return (($x*$y)&1)+($x*$y)%3; }
+ public function mask6($x, $y) { return ((($x*$y)&1)+($x*$y)%3)&1; }
+ public function mask7($x, $y) { return ((($x*$y)%3)+(($x+$y)&1))&1; }
+
+ //----------------------------------------------------------------------
+ private function generateMaskNo($maskNo, $width, $frame)
+ {
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if(ord($frame[$y][$x]) & 0x80) {
+ $bitMask[$y][$x] = 0;
+ } else {
+ $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
+ $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
+ }
+
+ }
+ }
+
+ return $bitMask;
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($bitFrame)
+ {
+ $codeArr = array();
+
+ foreach ($bitFrame as $line)
+ $codeArr[] = join('', $line);
+
+ return gzcompress(join("\n", $codeArr), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ $codeArr = array();
+
+ $codeLines = explode("\n", gzuncompress($code));
+ foreach ($codeLines as $line)
+ $codeArr[] = str_split($line);
+
+ return $codeArr;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false)
+ {
+ $b = 0;
+ $bitMask = array();
+
+ $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ $bitMask = self::unserial(file_get_contents($fileName));
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo))
+ mkdir(QR_CACHE_DIR.'mask_'.$maskNo);
+ file_put_contents($fileName, self::serial($bitMask));
+ }
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ }
+
+ if ($maskGenOnly)
+ return;
+
+ $d = $s;
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if($bitMask[$y][$x] == 1) {
+ $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
+ }
+ $b += (int)(ord($d[$y][$x]) & 1);
+ }
+ }
+
+ return $b;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMask($width, $frame, $maskNo, $level)
+ {
+ $masked = array_fill(0, $width, str_repeat("\0", $width));
+ $this->makeMaskNo($maskNo, $width, $frame, $masked);
+ $this->writeFormatInformation($width, $masked, $maskNo, $level);
+
+ return $masked;
+ }
+
+ //----------------------------------------------------------------------
+ public function calcN1N3($length)
+ {
+ $demerit = 0;
+
+ for($i=0; $i<$length; $i++) {
+
+ if($this->runLength[$i] >= 5) {
+ $demerit += (N1 + ($this->runLength[$i] - 5));
+ }
+ if($i & 1) {
+ if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) {
+ $fact = (int)($this->runLength[$i] / 3);
+ if(($this->runLength[$i-2] == $fact) &&
+ ($this->runLength[$i-1] == $fact) &&
+ ($this->runLength[$i+1] == $fact) &&
+ ($this->runLength[$i+2] == $fact)) {
+ if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) {
+ $demerit += N3;
+ } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) {
+ $demerit += N3;
+ }
+ }
+ }
+ }
+ }
+ return $demerit;
+ }
+
+ //----------------------------------------------------------------------
+ public function evaluateSymbol($width, $frame)
+ {
+ $head = 0;
+ $demerit = 0;
+
+ for($y=0; $y<$width; $y++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ $frameY = $frame[$y];
+
+ if ($y>0)
+ $frameYM = $frame[$y-1];
+
+ for($x=0; $x<$width; $x++) {
+ if(($x > 0) && ($y > 0)) {
+ $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
+ $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
+
+ if(($b22 | ($w22 ^ 1))&1) {
+ $demerit += N2;
+ }
+ }
+ if(($x == 0) && (ord($frameY[$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($x > 0) {
+ if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ for($x=0; $x<$width; $x++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ for($y=0; $y<$width; $y++) {
+ if($y == 0 && (ord($frame[$y][$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($y > 0) {
+ if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ return $demerit;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function mask($width, $frame, $level)
+ {
+ $minDemerit = PHP_INT_MAX;
+ $bestMaskNum = 0;
+ $bestMask = array();
+
+ $checked_masks = array(0,1,2,3,4,5,6,7);
+
+ if (QR_FIND_FROM_RANDOM !== false) {
+
+ $howManuOut = 8-(QR_FIND_FROM_RANDOM % 9);
+ for ($i = 0; $i < $howManuOut; $i++) {
+ $remPos = rand (0, count($checked_masks)-1);
+ unset($checked_masks[$remPos]);
+ $checked_masks = array_values($checked_masks);
+ }
+
+ }
+
+ $bestMask = $frame;
+
+ foreach($checked_masks as $i) {
+ $mask = array_fill(0, $width, str_repeat("\0", $width));
+
+ $demerit = 0;
+ $blacks = 0;
+ $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
+ $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
+ $blacks = (int)(100 * $blacks / ($width * $width));
+ $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
+ $demerit += $this->evaluateSymbol($width, $mask);
+
+ if($demerit < $minDemerit) {
+ $minDemerit = $demerit;
+ $bestMask = $mask;
+ $bestMaskNum = $i;
+ }
+ }
+
+ return $bestMask;
+ }
+
+ //----------------------------------------------------------------------
+ }
diff --git a/app/lib/phpqrcode/qrrscode.php b/app/lib/phpqrcode/qrrscode.php
new file mode 100644
index 00000000..591129a3
--- /dev/null
+++ b/app/lib/phpqrcode/qrrscode.php
@@ -0,0 +1,210 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsItem {
+
+ public $mm; // Bits per symbol
+ public $nn; // Symbols per block (= (1<= $this->nn) {
+ $x -= $this->nn;
+ $x = ($x >> $this->mm) + ($x & $this->nn);
+ }
+
+ return $x;
+ }
+
+ //----------------------------------------------------------------------
+ public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ // Common code for intializing a Reed-Solomon control block (char or int symbols)
+ // Copyright 2004 Phil Karn, KA9Q
+ // May be used under the terms of the GNU Lesser General Public License (LGPL)
+
+ $rs = null;
+
+ // Check parameter ranges
+ if($symsize < 0 || $symsize > 8) return $rs;
+ if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs;
+ if($prim <= 0 || $prim >= (1<<$symsize)) return $rs;
+ if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values!
+ if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding
+
+ $rs = new QRrsItem();
+ $rs->mm = $symsize;
+ $rs->nn = (1<<$symsize)-1;
+ $rs->pad = $pad;
+
+ $rs->alpha_to = array_fill(0, $rs->nn+1, 0);
+ $rs->index_of = array_fill(0, $rs->nn+1, 0);
+
+ // PHP style macro replacement ;)
+ $NN =& $rs->nn;
+ $A0 =& $NN;
+
+ // Generate Galois field lookup tables
+ $rs->index_of[0] = $A0; // log(zero) = -inf
+ $rs->alpha_to[$A0] = 0; // alpha**-inf = 0
+ $sr = 1;
+
+ for($i=0; $i<$rs->nn; $i++) {
+ $rs->index_of[$sr] = $i;
+ $rs->alpha_to[$i] = $sr;
+ $sr <<= 1;
+ if($sr & (1<<$symsize)) {
+ $sr ^= $gfpoly;
+ }
+ $sr &= $rs->nn;
+ }
+
+ if($sr != 1){
+ // field generator polynomial is not primitive!
+ $rs = NULL;
+ return $rs;
+ }
+
+ /* Form RS code generator polynomial from its roots */
+ $rs->genpoly = array_fill(0, $nroots+1, 0);
+
+ $rs->fcr = $fcr;
+ $rs->prim = $prim;
+ $rs->nroots = $nroots;
+ $rs->gfpoly = $gfpoly;
+
+ /* Find prim-th root of 1, used in decoding */
+ for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn)
+ ; // intentional empty-body loop!
+
+ $rs->iprim = (int)($iprim / $prim);
+ $rs->genpoly[0] = 1;
+
+ for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
+ $rs->genpoly[$i+1] = 1;
+
+ // Multiply rs->genpoly[] by @**(root + x)
+ for ($j = $i; $j > 0; $j--) {
+ if ($rs->genpoly[$j] != 0) {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)];
+ } else {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1];
+ }
+ }
+ // rs->genpoly[0] can never be zero
+ $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)];
+ }
+
+ // convert rs->genpoly[] to index form for quicker encoding
+ for ($i = 0; $i <= $nroots; $i++)
+ $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]];
+
+ return $rs;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode_rs_char($data, &$parity)
+ {
+ $MM =& $this->mm;
+ $NN =& $this->nn;
+ $ALPHA_TO =& $this->alpha_to;
+ $INDEX_OF =& $this->index_of;
+ $GENPOLY =& $this->genpoly;
+ $NROOTS =& $this->nroots;
+ $FCR =& $this->fcr;
+ $PRIM =& $this->prim;
+ $IPRIM =& $this->iprim;
+ $PAD =& $this->pad;
+ $A0 =& $NN;
+
+ $parity = array_fill(0, $NROOTS, 0);
+
+ for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) {
+
+ $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
+ if($feedback != $A0) {
+ // feedback term is non-zero
+
+ // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
+ // always be for the polynomials constructed by init_rs()
+ $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback);
+
+ for($j=1;$j<$NROOTS;$j++) {
+ $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS-$j])];
+ }
+ }
+
+ // Shift
+ array_shift($parity);
+ if($feedback != $A0) {
+ array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]);
+ } else {
+ array_push($parity, 0);
+ }
+ }
+ }
+ }
+
+ //##########################################################################
+
+ class QRrs {
+
+ public static $items = array();
+
+ //----------------------------------------------------------------------
+ public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ foreach(self::$items as $rs) {
+ if($rs->pad != $pad) continue;
+ if($rs->nroots != $nroots) continue;
+ if($rs->mm != $symsize) continue;
+ if($rs->gfpoly != $gfpoly) continue;
+ if($rs->fcr != $fcr) continue;
+ if($rs->prim != $prim) continue;
+
+ return $rs;
+ }
+
+ $rs = QRrsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
+ array_unshift(self::$items, $rs);
+
+ return $rs;
+ }
+ }
\ No newline at end of file
diff --git a/app/lib/phpqrcode/qrspec.php b/app/lib/phpqrcode/qrspec.php
new file mode 100644
index 00000000..92aea0c7
--- /dev/null
+++ b/app/lib/phpqrcode/qrspec.php
@@ -0,0 +1,592 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('QRSPEC_VERSION_MAX', 40);
+ define('QRSPEC_WIDTH_MAX', 177);
+
+ define('QRCAP_WIDTH', 0);
+ define('QRCAP_WORDS', 1);
+ define('QRCAP_REMINDER', 2);
+ define('QRCAP_EC', 3);
+
+ class QRspec {
+
+ public static $capacity = array(
+ array( 0, 0, 0, array( 0, 0, 0, 0)),
+ array( 21, 26, 0, array( 7, 10, 13, 17)), // 1
+ array( 25, 44, 7, array( 10, 16, 22, 28)),
+ array( 29, 70, 7, array( 15, 26, 36, 44)),
+ array( 33, 100, 7, array( 20, 36, 52, 64)),
+ array( 37, 134, 7, array( 26, 48, 72, 88)), // 5
+ array( 41, 172, 7, array( 36, 64, 96, 112)),
+ array( 45, 196, 0, array( 40, 72, 108, 130)),
+ array( 49, 242, 0, array( 48, 88, 132, 156)),
+ array( 53, 292, 0, array( 60, 110, 160, 192)),
+ array( 57, 346, 0, array( 72, 130, 192, 224)), //10
+ array( 61, 404, 0, array( 80, 150, 224, 264)),
+ array( 65, 466, 0, array( 96, 176, 260, 308)),
+ array( 69, 532, 0, array( 104, 198, 288, 352)),
+ array( 73, 581, 3, array( 120, 216, 320, 384)),
+ array( 77, 655, 3, array( 132, 240, 360, 432)), //15
+ array( 81, 733, 3, array( 144, 280, 408, 480)),
+ array( 85, 815, 3, array( 168, 308, 448, 532)),
+ array( 89, 901, 3, array( 180, 338, 504, 588)),
+ array( 93, 991, 3, array( 196, 364, 546, 650)),
+ array( 97, 1085, 3, array( 224, 416, 600, 700)), //20
+ array(101, 1156, 4, array( 224, 442, 644, 750)),
+ array(105, 1258, 4, array( 252, 476, 690, 816)),
+ array(109, 1364, 4, array( 270, 504, 750, 900)),
+ array(113, 1474, 4, array( 300, 560, 810, 960)),
+ array(117, 1588, 4, array( 312, 588, 870, 1050)), //25
+ array(121, 1706, 4, array( 336, 644, 952, 1110)),
+ array(125, 1828, 4, array( 360, 700, 1020, 1200)),
+ array(129, 1921, 3, array( 390, 728, 1050, 1260)),
+ array(133, 2051, 3, array( 420, 784, 1140, 1350)),
+ array(137, 2185, 3, array( 450, 812, 1200, 1440)), //30
+ array(141, 2323, 3, array( 480, 868, 1290, 1530)),
+ array(145, 2465, 3, array( 510, 924, 1350, 1620)),
+ array(149, 2611, 3, array( 540, 980, 1440, 1710)),
+ array(153, 2761, 3, array( 570, 1036, 1530, 1800)),
+ array(157, 2876, 0, array( 570, 1064, 1590, 1890)), //35
+ array(161, 3034, 0, array( 600, 1120, 1680, 1980)),
+ array(165, 3196, 0, array( 630, 1204, 1770, 2100)),
+ array(169, 3362, 0, array( 660, 1260, 1860, 2220)),
+ array(173, 3532, 0, array( 720, 1316, 1950, 2310)),
+ array(177, 3706, 0, array( 750, 1372, 2040, 2430)) //40
+ );
+
+ //----------------------------------------------------------------------
+ public static function getDataLength($version, $level)
+ {
+ return self::$capacity[$version][QRCAP_WORDS] - self::$capacity[$version][QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getECCLength($version, $level)
+ {
+ return self::$capacity[$version][QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getWidth($version)
+ {
+ return self::$capacity[$version][QRCAP_WIDTH];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getRemainder($version)
+ {
+ return self::$capacity[$version][QRCAP_REMINDER];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getMinimumVersion($size, $level)
+ {
+
+ for($i=1; $i<= QRSPEC_VERSION_MAX; $i++) {
+ $words = self::$capacity[$i][QRCAP_WORDS] - self::$capacity[$i][QRCAP_EC][$level];
+ if($words >= $size)
+ return $i;
+ }
+
+ return -1;
+ }
+
+ //######################################################################
+
+ public static $lengthTableBits = array(
+ array(10, 12, 14),
+ array( 9, 11, 13),
+ array( 8, 16, 16),
+ array( 8, 10, 12)
+ );
+
+ //----------------------------------------------------------------------
+ public static function lengthIndicator($mode, $version)
+ {
+ if ($mode == QR_MODE_STRUCTURE)
+ return 0;
+
+ if ($version <= 9) {
+ $l = 0;
+ } else if ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ return self::$lengthTableBits[$mode][$l];
+ }
+
+ //----------------------------------------------------------------------
+ public static function maximumWords($mode, $version)
+ {
+ if($mode == QR_MODE_STRUCTURE)
+ return 3;
+
+ if($version <= 9) {
+ $l = 0;
+ } else if($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ $bits = self::$lengthTableBits[$mode][$l];
+ $words = (1 << $bits) - 1;
+
+ if($mode == QR_MODE_KANJI) {
+ $words *= 2; // the number of bytes is required
+ }
+
+ return $words;
+ }
+
+ // Error correction code -----------------------------------------------
+ // Table of the error correction code (Reed-Solomon block)
+ // See Table 12-16 (pp.30-36), JIS X0510:2004.
+
+ public static $eccTable = array(
+ array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)),
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)),
+ array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5
+ array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)),
+ array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)),
+ array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)),
+ array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)),
+ array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), //10
+ array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)),
+ array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)),
+ array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)),
+ array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)),
+ array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), //15
+ array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)),
+ array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)),
+ array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)),
+ array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)),
+ array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), //20
+ array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)),
+ array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)),
+ array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)),
+ array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)),
+ array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), //25
+ array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)),
+ array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)),
+ array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)),
+ array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)),
+ array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), //30
+ array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)),
+ array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)),
+ array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)),
+ array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)),
+ array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), //35
+ array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)),
+ array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)),
+ array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)),
+ array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)),
+ array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),//40
+ );
+
+ //----------------------------------------------------------------------
+ // CACHEABLE!!!
+
+ public static function getEccSpec($version, $level, array &$spec)
+ {
+ if (count($spec) < 5) {
+ $spec = array(0,0,0,0,0);
+ }
+
+ $b1 = self::$eccTable[$version][$level][0];
+ $b2 = self::$eccTable[$version][$level][1];
+ $data = self::getDataLength($version, $level);
+ $ecc = self::getECCLength($version, $level);
+
+ if($b2 == 0) {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / $b1);
+ $spec[2] = (int)($ecc / $b1);
+ $spec[3] = 0;
+ $spec[4] = 0;
+ } else {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / ($b1 + $b2));
+ $spec[2] = (int)($ecc / ($b1 + $b2));
+ $spec[3] = $b2;
+ $spec[4] = $spec[1] + 1;
+ }
+ }
+
+ // Alignment pattern ---------------------------------------------------
+
+ // Positions of alignment patterns.
+ // This array includes only the second and the third position of the
+ // alignment patterns. Rest of them can be calculated from the distance
+ // between them.
+
+ // See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
+
+ public static $alignmentPattern = array(
+ array( 0, 0),
+ array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5
+ array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10
+ array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), //11-15
+ array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), //16-20
+ array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), //21-25
+ array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), //26-30
+ array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), //31-35
+ array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40
+ );
+
+
+ /** --------------------------------------------------------------------
+ * Put an alignment marker.
+ * @param frame
+ * @param width
+ * @param ox,oy center coordinate of the pattern
+ */
+ public static function putAlignmentMarker(array &$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xa1\xa1\xa1\xa1\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa0\xa1\xa0\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa1\xa1\xa1\xa1"
+ );
+
+ $yStart = $oy-2;
+ $xStart = $ox-2;
+
+ for($y=0; $y<5; $y++) {
+ QRstr::set($frame, $xStart, $yStart+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function putAlignmentPattern($version, &$frame, $width)
+ {
+ if($version < 2)
+ return;
+
+ $d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0];
+ if($d < 0) {
+ $w = 2;
+ } else {
+ $w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2);
+ }
+
+ if($w * $w - 3 == 1) {
+ $x = self::$alignmentPattern[$version][0];
+ $y = self::$alignmentPattern[$version][0];
+ self::putAlignmentMarker($frame, $x, $y);
+ return;
+ }
+
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=1; $x<$w - 1; $x++) {
+ self::putAlignmentMarker($frame, 6, $cx);
+ self::putAlignmentMarker($frame, $cx, 6);
+ $cx += $d;
+ }
+
+ $cy = self::$alignmentPattern[$version][0];
+ for($y=0; $y<$w-1; $y++) {
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=0; $x<$w-1; $x++) {
+ self::putAlignmentMarker($frame, $cx, $cy);
+ $cx += $d;
+ }
+ $cy += $d;
+ }
+ }
+
+ // Version information pattern -----------------------------------------
+
+ // Version information pattern (BCH coded).
+ // See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
+
+ // size: [QRSPEC_VERSION_MAX - 6]
+
+ public static $versionPattern = array(
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
+ 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
+ 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
+ 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
+ 0x27541, 0x28c69
+ );
+
+ //----------------------------------------------------------------------
+ public static function getVersionPattern($version)
+ {
+ if($version < 7 || $version > QRSPEC_VERSION_MAX)
+ return 0;
+
+ return self::$versionPattern[$version -7];
+ }
+
+ // Format information --------------------------------------------------
+ // See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib)
+
+ public static $formatInfo = array(
+ array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976),
+ array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0),
+ array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed),
+ array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b)
+ );
+
+ public static function getFormatInfo($mask, $level)
+ {
+ if($mask < 0 || $mask > 7)
+ return 0;
+
+ if($level < 0 || $level > 3)
+ return 0;
+
+ return self::$formatInfo[$level][$mask];
+ }
+
+ // Frame ---------------------------------------------------------------
+ // Cache of initial frames.
+
+ public static $frames = array();
+
+ /** --------------------------------------------------------------------
+ * Put a finder pattern.
+ * @param frame
+ * @param width
+ * @param ox,oy upper-left coordinate of the pattern
+ */
+ public static function putFinderPattern(&$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
+ );
+
+ for($y=0; $y<7; $y++) {
+ QRstr::set($frame, $ox, $oy+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function createFrame($version)
+ {
+ $width = self::$capacity[$version][QRCAP_WIDTH];
+ $frameLine = str_repeat ("\0", $width);
+ $frame = array_fill(0, $width, $frameLine);
+
+ // Finder pattern
+ self::putFinderPattern($frame, 0, 0);
+ self::putFinderPattern($frame, $width - 7, 0);
+ self::putFinderPattern($frame, 0, $width - 7);
+
+ // Separator
+ $yOffset = $width - 7;
+
+ for($y=0; $y<7; $y++) {
+ $frame[$y][7] = "\xc0";
+ $frame[$y][$width - 8] = "\xc0";
+ $frame[$yOffset][7] = "\xc0";
+ $yOffset++;
+ }
+
+ $setPattern = str_repeat("\xc0", 8);
+
+ QRstr::set($frame, 0, 7, $setPattern);
+ QRstr::set($frame, $width-8, 7, $setPattern);
+ QRstr::set($frame, 0, $width - 8, $setPattern);
+
+ // Format info
+ $setPattern = str_repeat("\x84", 9);
+ QRstr::set($frame, 0, 8, $setPattern);
+ QRstr::set($frame, $width - 8, 8, $setPattern, 8);
+
+ $yOffset = $width - 8;
+
+ for($y=0; $y<8; $y++,$yOffset++) {
+ $frame[$y][8] = "\x84";
+ $frame[$yOffset][8] = "\x84";
+ }
+
+ // Timing pattern
+
+ for($i=1; $i<$width-15; $i++) {
+ $frame[6][7+$i] = chr(0x90 | ($i & 1));
+ $frame[7+$i][6] = chr(0x90 | ($i & 1));
+ }
+
+ // Alignment pattern
+ self::putAlignmentPattern($version, $frame, $width);
+
+ // Version information
+ if($version >= 7) {
+ $vinf = self::getVersionPattern($version);
+
+ $v = $vinf;
+
+ for($x=0; $x<6; $x++) {
+ for($y=0; $y<3; $y++) {
+ $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+
+ $v = $vinf;
+ for($y=0; $y<6; $y++) {
+ for($x=0; $x<3; $x++) {
+ $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+ }
+
+ // and a little bit...
+ $frame[$width - 8][8] = "\x81";
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function debug($frame, $binary_mode = false)
+ {
+ if ($binary_mode) {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join(' ', explode('0', $frameLine));
+ $frameLine = join('██', explode('1', $frameLine));
+ }
+
+ ?>
+
+ ';
+ echo join(" ", $frame);
+ echo ' ';
+
+ } else {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join(' ', explode("\xc0", $frameLine));
+ $frameLine = join('▒ ', explode("\xc1", $frameLine));
+ $frameLine = join(' ', explode("\xa0", $frameLine));
+ $frameLine = join('▒ ', explode("\xa1", $frameLine));
+ $frameLine = join('◇ ', explode("\x84", $frameLine)); //format 0
+ $frameLine = join('◆ ', explode("\x85", $frameLine)); //format 1
+ $frameLine = join('☢ ', explode("\x81", $frameLine)); //special bit
+ $frameLine = join(' ', explode("\x90", $frameLine)); //clock 0
+ $frameLine = join('◷ ', explode("\x91", $frameLine)); //clock 1
+ $frameLine = join(' ', explode("\x88", $frameLine)); //version
+ $frameLine = join('▒ ', explode("\x89", $frameLine)); //version
+ $frameLine = join('♦', explode("\x01", $frameLine));
+ $frameLine = join('⋅', explode("\0", $frameLine));
+ }
+
+ ?>
+
+ ";
+ echo join(" ", $frame);
+ echo " ";
+
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($frame)
+ {
+ return gzcompress(join("\n", $frame), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ return explode("\n", gzuncompress($code));
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFrame($version)
+ {
+ if($version < 1 || $version > QRSPEC_VERSION_MAX)
+ return null;
+
+ if(!isset(self::$frames[$version])) {
+
+ $fileName = QR_CACHE_DIR.'frame_'.$version.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ self::$frames[$version] = self::unserial(file_get_contents($fileName));
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ file_put_contents($fileName, self::serial(self::$frames[$version]));
+ }
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ }
+ }
+
+ if(is_null(self::$frames[$version]))
+ return null;
+
+ return self::$frames[$version];
+ }
+
+ //----------------------------------------------------------------------
+ public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; }
+ public static function rsBlockNum1($spec) { return $spec[0]; }
+ public static function rsDataCodes1($spec) { return $spec[1]; }
+ public static function rsEccCodes1($spec) { return $spec[2]; }
+ public static function rsBlockNum2($spec) { return $spec[3]; }
+ public static function rsDataCodes2($spec) { return $spec[4]; }
+ public static function rsEccCodes2($spec) { return $spec[2]; }
+ public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); }
+ public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; }
+
+ }
\ No newline at end of file
diff --git a/app/lib/phpqrcode/qrsplit.php b/app/lib/phpqrcode/qrsplit.php
new file mode 100644
index 00000000..d75b8273
--- /dev/null
+++ b/app/lib/phpqrcode/qrsplit.php
@@ -0,0 +1,311 @@
+
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+ class QRsplit {
+
+ public $dataStr = '';
+ public $input;
+ public $modeHint;
+
+ //----------------------------------------------------------------------
+ public function __construct($dataStr, $input, $modeHint)
+ {
+ $this->dataStr = $dataStr;
+ $this->input = $input;
+ $this->modeHint = $modeHint;
+ }
+
+ //----------------------------------------------------------------------
+ public static function isdigitat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
+ }
+
+ //----------------------------------------------------------------------
+ public static function isalnumat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return (QRinput::lookAnTable(ord($str[$pos])) >= 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function identifyMode($pos)
+ {
+ if ($pos >= strlen($this->dataStr))
+ return QR_MODE_NUL;
+
+ $c = $this->dataStr[$pos];
+
+ if(self::isdigitat($this->dataStr, $pos)) {
+ return QR_MODE_NUM;
+ } else if(self::isalnumat($this->dataStr, $pos)) {
+ return QR_MODE_AN;
+ } else if($this->modeHint == QR_MODE_KANJI) {
+
+ if ($pos+1 < strlen($this->dataStr))
+ {
+ $d = $this->dataStr[$pos+1];
+ $word = (ord($c) << 8) | ord($d);
+ if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
+ return QR_MODE_KANJI;
+ }
+ }
+ }
+
+ return QR_MODE_8;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatNum()
+ {
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+ while(self::isdigitat($this->dataStr, $p)) {
+ $p++;
+ }
+
+ $run = $p;
+ $mode = $this->identifyMode($p);
+
+ if($mode == QR_MODE_8) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ if($mode == QR_MODE_AN) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsModeAn(1) // + 4 + la
+ - QRinput::estimateBitsModeAn($run + 1);// - 4 - la
+ if($dif > 0) {
+ return $this->eatAn();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_NUM, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatAn()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+
+ while(self::isalnumat($this->dataStr, $p)) {
+ if(self::isdigitat($this->dataStr, $p)) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+
+ $dif = QRinput::estimateBitsModeAn($p) // + 4 + la
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsModeAn($q); // - 4 - la
+
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+
+ if(!self::isalnumat($this->dataStr, $p)) {
+ $dif = QRinput::estimateBitsModeAn($run) + 4 + $la
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_AN, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatKanji()
+ {
+ $p = 0;
+
+ while($this->identifyMode($p) == QR_MODE_KANJI) {
+ $p += 2;
+ }
+
+ $ret = $this->input->append(QR_MODE_KANJI, $p, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eat8()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 1;
+ $dataStrLen = strlen($this->dataStr);
+
+ while($p < $dataStrLen) {
+
+ $mode = $this->identifyMode($p);
+ if($mode == QR_MODE_KANJI) {
+ break;
+ }
+ if($mode == QR_MODE_NUM) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else if($mode == QR_MODE_AN) {
+ $q = $p;
+ while(self::isalnumat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeAn($q - $p) + 4 + $la
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+ $ret = $this->input->append(QR_MODE_8, $run, str_split($this->dataStr));
+
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function splitString()
+ {
+ while (strlen($this->dataStr) > 0)
+ {
+ if($this->dataStr == '')
+ return 0;
+
+ $mode = $this->identifyMode(0);
+
+ switch ($mode) {
+ case QR_MODE_NUM: $length = $this->eatNum(); break;
+ case QR_MODE_AN: $length = $this->eatAn(); break;
+ case QR_MODE_KANJI:
+ if ($hint == QR_MODE_KANJI)
+ $length = $this->eatKanji();
+ else $length = $this->eat8();
+ break;
+ default: $length = $this->eat8(); break;
+
+ }
+
+ if($length == 0) return 0;
+ if($length < 0) return -1;
+
+ $this->dataStr = substr($this->dataStr, $length);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function toUpper()
+ {
+ $stringLen = strlen($this->dataStr);
+ $p = 0;
+
+ while ($p<$stringLen) {
+ $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint);
+ if($mode == QR_MODE_KANJI) {
+ $p += 2;
+ } else {
+ if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) {
+ $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
+ }
+ $p++;
+ }
+ }
+
+ return $this->dataStr;
+ }
+
+ //----------------------------------------------------------------------
+ public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true)
+ {
+ if(is_null($string) || $string == '\0' || $string == '') {
+ throw new Exception('empty string!!!');
+ }
+
+ $split = new QRsplit($string, $input, $modeHint);
+
+ if(!$casesensitive)
+ $split->toUpper();
+
+ return $split->splitString();
+ }
+ }
\ No newline at end of file
diff --git a/app/lib/phpqrcode/qrtools.php b/app/lib/phpqrcode/qrtools.php
new file mode 100644
index 00000000..3012db49
--- /dev/null
+++ b/app/lib/phpqrcode/qrtools.php
@@ -0,0 +1,172 @@
+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRtools {
+
+ //----------------------------------------------------------------------
+ public static function binarize($frame)
+ {
+ $len = count($frame);
+ foreach ($frame as &$frameLine) {
+
+ for($i=0; $i<$len; $i++) {
+ $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
+ }
+ }
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function tcpdfBarcodeArray($code, $mode = 'QR,L', $tcPdfVersion = '4.5.037')
+ {
+ $barcode_array = array();
+
+ if (!is_array($mode))
+ $mode = explode(',', $mode);
+
+ $eccLevel = 'L';
+
+ if (count($mode) > 1) {
+ $eccLevel = $mode[1];
+ }
+
+ $qrTab = QRcode::text($code, false, $eccLevel);
+ $size = count($qrTab);
+
+ $barcode_array['num_rows'] = $size;
+ $barcode_array['num_cols'] = $size;
+ $barcode_array['bcode'] = array();
+
+ foreach ($qrTab as $line) {
+ $arrAdd = array();
+ foreach(str_split($line) as $char)
+ $arrAdd[] = ($char=='1')?1:0;
+ $barcode_array['bcode'][] = $arrAdd;
+ }
+
+ return $barcode_array;
+ }
+
+ //----------------------------------------------------------------------
+ public static function clearCache()
+ {
+ self::$frames = array();
+ }
+
+ //----------------------------------------------------------------------
+ public static function buildCache()
+ {
+ QRtools::markTime('before_build_cache');
+
+ $mask = new QRmask();
+ for ($a=1; $a <= QRSPEC_VERSION_MAX; $a++) {
+ $frame = QRspec::newFrame($a);
+ if (QR_IMAGE) {
+ $fileName = QR_CACHE_DIR.'frame_'.$a.'.png';
+ QRimage::png(self::binarize($frame), $fileName, 1, 0);
+ }
+
+ $width = count($frame);
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+ for ($maskNo=0; $maskNo<8; $maskNo++)
+ $mask->makeMaskNo($maskNo, $width, $frame, $bitMask, true);
+ }
+
+ QRtools::markTime('after_build_cache');
+ }
+
+ //----------------------------------------------------------------------
+ public static function log($outfile, $err)
+ {
+ if (QR_LOG_DIR !== false) {
+ if ($err != '') {
+ if ($outfile !== false) {
+ file_put_contents(QR_LOG_DIR.basename($outfile).'-errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ } else {
+ file_put_contents(QR_LOG_DIR.'errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function dumpMask($frame)
+ {
+ $width = count($frame);
+ for($y=0;$y<$width;$y++) {
+ for($x=0;$x<$width;$x++) {
+ echo ord($frame[$y][$x]).',';
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function markTime($markerId)
+ {
+ list($usec, $sec) = explode(" ", microtime());
+ $time = ((float)$usec + (float)$sec);
+
+ if (!isset($GLOBALS['qr_time_bench']))
+ $GLOBALS['qr_time_bench'] = array();
+
+ $GLOBALS['qr_time_bench'][$markerId] = $time;
+ }
+
+ //----------------------------------------------------------------------
+ public static function timeBenchmark()
+ {
+ self::markTime('finish');
+
+ $lastTime = 0;
+ $startTime = 0;
+ $p = 0;
+
+ echo '
+ BENCHMARK
+ ';
+
+ foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) {
+ if ($p > 0) {
+ echo 'till '.$markerId.': '.number_format($thisTime-$lastTime, 6).'s ';
+ } else {
+ $startTime = $thisTime;
+ }
+
+ $p++;
+ $lastTime = $thisTime;
+ }
+
+ echo '
+ TOTAL: '.number_format($lastTime-$startTime, 6).'s
+
+
';
+ }
+
+ }
+
+ //##########################################################################
+
+ QRtools::markTime('start');
+
\ No newline at end of file
diff --git a/app/lib/phpqrcode/tools/merge.bat b/app/lib/phpqrcode/tools/merge.bat
new file mode 100644
index 00000000..b60a4853
--- /dev/null
+++ b/app/lib/phpqrcode/tools/merge.bat
@@ -0,0 +1,2 @@
+php ./merge.php
+pause
\ No newline at end of file
diff --git a/app/lib/phpqrcode/tools/merge.php b/app/lib/phpqrcode/tools/merge.php
new file mode 100644
index 00000000..19d338b3
--- /dev/null
+++ b/app/lib/phpqrcode/tools/merge.php
@@ -0,0 +1,70 @@
+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ $QR_BASEDIR = dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR;
+ $QR_TOOLSDIR = dirname(__FILE__).DIRECTORY_SEPARATOR;
+
+ $outputFile = $QR_BASEDIR.'phpqrcode.php';
+
+ // Required libs
+
+ $fileList = array(
+ $QR_BASEDIR.'qrconst.php',
+ $QR_TOOLSDIR.'merged_config.php',
+ $QR_BASEDIR.'qrtools.php',
+ $QR_BASEDIR.'qrspec.php',
+ $QR_BASEDIR.'qrimage.php',
+ $QR_BASEDIR.'qrinput.php',
+ $QR_BASEDIR.'qrbitstream.php',
+ $QR_BASEDIR.'qrsplit.php',
+ $QR_BASEDIR.'qrrscode.php',
+ $QR_BASEDIR.'qrmask.php',
+ $QR_BASEDIR.'qrencode.php'
+ );
+
+ $headerFile = $QR_TOOLSDIR.'merged_header.php';
+ $versionFile = $QR_BASEDIR.'VERSION';
+
+ $outputCode = '';
+
+ foreach($fileList as $fileName) {
+ $outputCode .= "\n\n".'//---- '.basename($fileName).' -----------------------------'."\n\n";
+ $anotherCode = file_get_contents($fileName);
+ $anotherCode = preg_replace ('/^<\?php/', '', $anotherCode);
+ $anotherCode = preg_replace ('/\?>\*$/', '', $anotherCode);
+ $outputCode .= "\n\n".$anotherCode."\n\n";
+ }
+
+ $versionDataEx = explode("\n", file_get_contents($versionFile));
+
+ $outputContents = file_get_contents($headerFile);
+ $outputContents .= "\n\n/*\n * Version: ".trim($versionDataEx[0])."\n * Build: ".trim($versionDataEx[1])."\n */\n\n";
+ $outputContents .= $outputCode;
+
+ file_put_contents($outputFile, $outputContents);
+
+
\ No newline at end of file
diff --git a/app/lib/phpqrcode/tools/merge.sh b/app/lib/phpqrcode/tools/merge.sh
new file mode 100644
index 00000000..e4c2fbcb
--- /dev/null
+++ b/app/lib/phpqrcode/tools/merge.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+php ./merge.php
\ No newline at end of file
diff --git a/app/lib/phpqrcode/tools/merged_config.php b/app/lib/phpqrcode/tools/merged_config.php
new file mode 100644
index 00000000..55ddb450
--- /dev/null
+++ b/app/lib/phpqrcode/tools/merged_config.php
@@ -0,0 +1,17 @@
+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
\ No newline at end of file
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/models/StationtypeModel.inc b/app/models/StationtypeModel.inc
new file mode 100644
index 00000000..2121d9ff
--- /dev/null
+++ b/app/models/StationtypeModel.inc
@@ -0,0 +1,176 @@
+
+ * @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 StationtypeModel.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class StationtypeModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Copy a Station.
+ *
+ * @param int $userId ID of creating user
+ * @param int $sourceStationId ID of Station to copy from
+ * @param int $targetStationId ID of Station to copy to
+ * @param int $seminaryMediaIds Mapping of SeminaryMedia-IDs from source Seminary to targetSeminary
+ */
+ public abstract function copyStation($userId, $sourceStationId, $targetStationId, $seminaryMediaIds);
+
+
+ /**
+ * Load a Model.
+ *
+ * @static
+ * @throws \hhu\z\exceptions\StationtypeModelNotFoundException
+ * @throws \hhu\z\exceptions\StationtypeModelNotValidException
+ * @param string $modelName Name of the StationtypeModel 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\StationtypeModelNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \hhu\z\exceptions\StationtypeModelNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a StationtypeModel (Factory Pattern).
+ *
+ * @static
+ * @param string $stationtypeName Name of the StationtypeModel to instantiate
+ */
+ public static function factory($stationtypeName)
+ {
+ // Determine full classname
+ $className = self::getClassName($stationtypeName);
+
+ // Construct and return Model
+ return new $className();
+ }
+
+
+ /**
+ * Determine the Model-classname for the given Stationtype-name.
+ *
+ * @static
+ * @param string $stationtypeName Stationtype-name to get Model-classname of
+ * @return string Classname for the Stationtype-name
+ */
+ private static function getClassName($stationtypeName)
+ {
+ $className = \nre\core\ClassLoader::concatClassNames(
+ $stationtypeName,
+ \nre\core\ClassLoader::stripClassType(
+ \nre\core\ClassLoader::stripNamespace(
+ get_class()
+ )
+ ),
+ 'model'
+ );
+
+
+ return \nre\configs\AppConfig::$app['namespace']."stationtypes\\$className";
+ }
+
+
+ /**
+ * Load the class of a StationtypeModel.
+ *
+ * @static
+ * @throws \nre\exceptions\ClassNotFoundException
+ * @param string $stationtypeName Name of the StationtypeModel to load
+ * @param string $fullClassName Name of the class to load
+ */
+ private static function loadClass($stationtypeName, $fullClassName)
+ {
+ // Determine folder to look in
+ $className = explode('\\', $fullClassName);
+ $className = array_pop($className);
+
+ // Determine filename
+ $fileName = ROOT.DS.
+ \nre\configs\AppConfig::$dirs['stationtypes'].DS.
+ strtolower($stationtypeName).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 StationtypeModel-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 StationtypeModel.
+ *
+ * @throws \nre\exceptions\DatamodelException
+ * @throws \nre\exceptions\DriverNotFoundException
+ * @throws \nre\exceptions\DriverNotValidException
+ * @throws \hhu\z\exceptions\StationtypeModelNotValidException
+ * @throws \hhu\z\exceptions\StationtypeModelNotFoundException
+ */
+ 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/app/views/StationtypeView.inc b/app/views/StationtypeView.inc
new file mode 100644
index 00000000..99bb3c0e
--- /dev/null
+++ b/app/views/StationtypeView.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\views;
+
+
+ /**
+ * Abstract class for implementing a StationtypeView.
+ *
+ * @author Oliver Hanraths
+ */
+ class StationtypeView extends \nre\core\View
+ {
+
+
+
+
+ /**
+ * Load and instantiate the StationtypeView of a StationtypeAgent.
+ *
+ * @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 StationtypeView($layoutName, $agentName, $action, $isToplevel);
+ }
+
+
+
+
+ /**
+ * Construct a new StationtypeView.
+ *
+ * @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['stationtypes'].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..a9b6f7f3
--- /dev/null
+++ b/configs/AppConfig.inc
@@ -0,0 +1,471 @@
+
+ * @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',
+ 'stationtypes' => 'stationtypes',
+ '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
+ ),
+ 'charactergroupsqueststation' => array(
+ 'width' => 100,
+ 'height' => 100
+ )
+ );
+
+
+ /**
+ * 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
+ )
+ ),
+ 'stationimages' => array(
+ array(
+ 'mimetype' => 'image/jpeg',
+ 'size' => 1048576
+ ),
+ array(
+ 'mimetype' => 'image/png',
+ 'size' => 2097152
+ )
+ ),
+ 'stationavs' => array(
+ array(
+ 'mimetype' => 'audio/mpeg',
+ 'size' => 2097152
+ ),
+ array(
+ 'mimetype' => 'audio/ogg',
+ 'size' => 2097152
+ ),
+ array(
+ 'mimetype' => 'audio/opus',
+ 'size' => 2097152
+ ),
+ array(
+ 'mimetype' => 'video/mp4',
+ 'size' => 2097152
+ ),
+ array(
+ 'mimetype' => 'video/webm',
+ 'size' => 2097152
+ )
+ )
+ );
+
+
+ /**
+ * 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
+ ),
+ 'title_male' => array(
+ 'minlength' => 3,
+ 'maxlength' => 24
+ ),
+ 'title_female' => array(
+ 'minlength' => 3,
+ 'maxlength' => 24
+ )
+ );
+
+
+ /**
+ * 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('^charactertitles/([^/]+)/?$', 'charactertitles/index/$1', true),
+ array('^charactertitles/([^/]+)/create/?$', 'charactertitles/create/$1', true),
+ array('^charactertitles/([^/]+)/([^/]+)/(edit|delete)/?$', 'charactertitles/$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('^charactergroupsqueststations/([^/]+)/([^/]+)/([^/]+)/?$', 'charactergroupsqueststations/index/$1/$2/$3?layout=ajax', true),
+ array('^charactergroupsqueststations/([^/]+)/([^/]+)/([^/]+)/create/?$', 'charactergroupsqueststations/create/$1/$2/$3', true),
+ array('^charactergroupsqueststations/([^/]+)/([^/]+)/([^/]+)/([^/]+)/(edit|edittask|delete)/?$', 'charactergroupsqueststations/$5/$1/$2/$3/$4', true),
+ array('^charactergroupsqueststations/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$', 'charactergroupsqueststations/station/$1/$2/$3/$4', true),
+ array('^charactergroupsachievements/([^/]+)/([^/]+)/(create|manage)/?$', 'charactergroupsachievements/$3/$1/$2', true),
+ array('^charactergroupsachievements/([^/]+)/([^/]+)/([^/]+)/(edit|manage|moveup|movedown|delete)/?$', 'charactergroupsachievements/$4/$1/$2/$3', true),
+ array('^charactergroupsachievements/([^/]+)/([^/]+)/([^/]+)/?$', 'charactergroupsachievements/achievement/$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),
+ array('^qrcodes/(.*)$', 'qrcodes/$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('^charactertitles/index/([^/]+)$', 'charactertitles/$1', true),
+ array('^charactertitles/create/([^/]+)$', 'charactertitles/$1/create', true),
+ array('^charactertitles/(edit|delete)/([^/]+)/([^/]+)$', 'charactertitles/$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('^charactergroupsqueststations/index/(.*)$', 'charactergroupsqueststations/$1', true),
+ array('^charactergroupsqueststations/station/(.*)$', 'charactergroupsqueststations/$1', true),
+ array('^charactergroupsqueststations/(create|edit|edittask|delete)/(.*)$', 'charactergroupsqueststations/$2/$1', true),
+ array('^charactergroupsachievements/(create|manage)/(.*)$', 'charactergroupsachievements/$2/$1', true),
+ array('^charactergroupsachievements/(edit|moveup|movedown|delete)/(.*)$', 'charactergroupsachievements/$2/$1', true),
+ array('^charactergroupsachievements/achievement/(.*)$', 'charactergroupsachievements/$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..66e005d2
--- /dev/null
+++ b/controllers/AchievementsController.inc
@@ -0,0 +1,981 @@
+
+ * @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'),
+ 'manage' => 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'),
+ 'manage' => 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['icons'];
+
+ // 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['icons'];
+
+ // 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;
+ // QR-Code conditions
+ case 'qrcode':
+ $conditions = $this->Achievements->getAchievementConditionsQrcode($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;
+ // QR-Code conditions
+ case 'qrcode':
+ if(array_key_exists($condition['id'], $deletes)) {
+ $this->Achievements->deleteAchievementConditionQrcode($condition['id']);
+ }
+ 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;
+ // QR-code conditions
+ case 'qrcode':
+ if(array_key_exists('qrcode', $condition))
+ {
+ $this->Achievements->addAchievementConditionQrcode(
+ $this->Auth->getUserId(),
+ $achievement['id']
+ );
+ }
+ 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..f8307d91
--- /dev/null
+++ b/controllers/CharactergroupsController.inc
@@ -0,0 +1,785 @@
+
+ * @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', 'charactergroupsachievements', 'characters', 'avatars', 'charactertitles', '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']);
+
+ // Get Achievements
+ $achievements = array();
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) {
+ $achievements = $this->Charactergroupsachievements->getAchievementsForCharactergroupsgroup(
+ $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);
+ $this->set('achievements', $achievements);
+ }
+
+
+ /**
+ * 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($seminary['id'], $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 and titles
+ foreach($group['characters'] as &$character)
+ {
+ // Avatar
+ $avatar = $this->Avatars->getAvatarById($character['avatar_id']);
+ if(!is_null($avatar['small_avatarpicture_id'])) {
+ $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']);
+ }
+
+ // Title
+ if(!is_null($character['charactertitle_id']) && !is_null($character['gender']))
+ {
+ $title = $this->Charactertitles->getTitleById($character['charactertitle_id']);
+ $character['title'] = $title[($character['gender']) ? 'title_male' : 'title_female'];
+ }
+ }
+
+ // Get Character groups Quests
+ $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']);
+
+ // Get Achievements
+ $achievements = $this->Charactergroupsachievements->getAchievedAchievementsForGroup(
+ $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);
+ $this->set('achievements', $achievements);
+ }
+
+
+ /**
+ * 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/CharactergroupsachievementsController.inc b/controllers/CharactergroupsachievementsController.inc
new file mode 100644
index 00000000..ca5b36e3
--- /dev/null
+++ b/controllers/CharactergroupsachievementsController.inc
@@ -0,0 +1,622 @@
+
+ * @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 Charactegroupsachievements.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsachievementsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'charactergroupsachievements', 'charactergroups', 'media');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation', 'notification');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'achievement' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator', 'user'),
+ 'moveup' => array('admin', 'moderator', 'user'),
+ 'movedown' => array('admin', 'moderator', 'user'),
+ 'edit' => array('admin', 'moderator', 'user'),
+ 'manage' => array('admin', 'moderator', 'user'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'achievement' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin'),
+ 'moveup' => array('admin', 'moderator'),
+ 'movedown' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'manage' => array('admin', 'moderator'),
+ 'delete' => array('admin')
+ );
+
+
+
+
+ /**
+ * Action: achievement.
+ *
+ * Let a Character group achieve an achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $groupsgroupUrl URL-title of Character groups-group
+ * @param string $achievementHash Hash of Character groups achievement
+ */
+ public function achievement($seminaryUrl, $groupsgroupUrl, $achievementHash)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByHash(
+ $achievementHash
+ );
+
+ // Get Character group(s)
+ $charactergroups = null;
+ $charactergroup = null;
+ $notify = true;
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) {
+ $charactergroups = $this->Charactergroups->getGroupsForGroupsgroup($groupsgroup['id']);
+ $notify = false;
+ }
+ else
+ {
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+ $charactergroups = $this->Charactergroups->getGroupsForCharacter($character['id']);
+ if(count($charactergroups) == 1) {
+ $charactergroup = $charactergroups[0];
+ }
+ }
+ // Select group by parameter
+ $selectedCharactergroupId = $this->request->getPostParam('charactergroup');
+ if(!is_null($selectedCharactergroupId))
+ {
+ $selectedCharactergroupId = intval($selectedCharactergroupId);
+ foreach($charactergroups as &$group) {
+ if($group['id'] == $selectedCharactergroupId) {
+ $charactergroup = $group;
+ break;
+ }
+ }
+ }
+
+ // Set Achievement achieved for Character group
+ if(!is_null($charactergroup))
+ {
+ if(!$this->Charactergroupsachievements->hasCharactergroupAchievedAchievement(
+ $achievement['id'],
+ $charactergroup['id']
+ )) {
+ // Set achieved-status
+ $this->Charactergroupsachievements->setAchievementAchieved(
+ $achievement['id'],
+ $charactergroup['id']
+ );
+
+ // Set notification
+ if($notify) {
+ $this->Notification->addNotification(
+ \hhu\z\controllers\components\NotificationComponent::TYPE_CHARACTERGROUPSACHIEVEMENT,
+ $achievement['title'],
+ $this->linker->link(
+ array(
+ 'charactergroups',
+ 'group',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $charactergroup['url']
+ ), 0, true, null, true, 'achievements'
+ ),
+ (!is_null($achievement['achievementsmedia_id']) ? $this->linker->link(
+ array(
+ 'media',
+ 'charactergroupsachievement',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $achievement['url']
+ )
+ ) : null)
+ );
+ }
+ }
+
+ // Redirect
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'charactergroups',
+ 'group',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $charactergroup['url']
+ ), 0, true, null, true, 'achievements'
+ )
+ );
+ }
+
+
+ // TODO Set title
+ $this->addTitle($achievement['title']);
+ $this->addTitle(sprintf(_('%s-Achievements'), $groupsgroup['name']));
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('achievement', $achievement);
+ $this->set('charactergroups', $charactergroups);
+ $this->set('charactergroup', $charactergroup);
+ }
+
+
+ /**
+ * Action: manage.
+ *
+ * Manage Achievements of a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of Seminary
+ * @param string $groupsgroupUrl URL-title of Character groups-group
+ */
+ public function manage($seminaryUrl, $groupsgroupUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Achievements
+ $achievements = $this->Charactergroupsachievements->getAchievementsForCharactergroupsgroup(
+ $groupsgroup['id']
+ );
+
+
+ // TODO Set title
+ $this->addTitleLocalized('Manage Achievements');
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('achievements', $achievements);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $groupsgroupUrl URL-title of 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 allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+
+ // Values
+ $title = '';
+ $description = '';
+ $fields = array('title');
+ $validation = array();
+
+ // Create Achievement
+ 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->Charactergroupsachievements->achievementTitleExists($groupsgroup['id'], $title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $description = $this->request->getPostParam('description');
+
+ // 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 Achievement
+ if($validation === true)
+ {
+ $achievementId = $this->Charactergroupsachievements->createAchievement(
+ $this->Auth->getUserId(),
+ $groupsgroup['id'],
+ $title,
+ $description
+ );
+ $achievement = $this->Charactergroupsachievements->getAchievementById($achievementId);
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createAchievementMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroupsachievement-%d', $achievement['id']),
+ $achievement['title'],
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroupsachievements->setIconForAchievement(
+ $achievement['id'],
+ $mediaId
+ );
+ }
+ }
+
+ // Redirect to condition editing
+ $this->redirect(
+ $this->linker->link(array(
+ 'manage',
+ $seminary['url'],
+ $groupsgroup['url']
+ ), 1, true, null, true, 'achievements'
+ ));
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // TODO Set title
+ $this->addTitleLocalized('Create Achievement');
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('title', $title);
+ $this->set('description', $description);
+ $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 $groupsgroupUrl URL-title of Character groups-group
+ * @param string $achievementUrl URL-title of Achievement
+ */
+ public function moveup($seminaryUrl, $groupsgroupUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByUrl($groupsgroup['id'], $achievementUrl);
+
+ // Set position
+ $this->Charactergroupsachievements->moveAchievement($achievement, true);
+
+
+ // Redirect
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'manage',
+ $seminary['url'],
+ $groupsgroup['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 $groupsgroupUrl URL-title of Character groups-group
+ * @param string $achievementUrl URL-title of Achievement
+ */
+ public function movedown($seminaryUrl, $groupsgroupUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByUrl($groupsgroup['id'], $achievementUrl);
+
+ // Set position
+ $this->Charactergroupsachievements->moveAchievement($achievement, false);
+
+
+ // Redirect
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'manage',
+ $seminary['url'],
+ $groupsgroup['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 $groupsgroupUrl URL-title of Character groups-group
+ * @param string $achievementUrl URL-title of Achievement to edit
+ */
+ public function edit($seminaryUrl, $groupsgroupUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByUrl(
+ $groupsgroup['id'],
+ $achievementUrl
+ );
+
+ // Get allowed mimetypes
+ $mimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+
+ // Values
+ $title = $achievement['title'];
+ $description = $achievement['description'];
+ $fields = array('title');
+ $validation = array();
+
+ // Edit 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->Charactergroupsachievements->achievementTitleExists($groupsgroup['id'], $title, $achievement['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $description = $this->request->getPostParam('description');
+
+ // 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 Achievement
+ if($validation === true)
+ {
+ $this->Charactergroupsachievements->editAchievement(
+ $achievement['id'],
+ $title,
+ $description
+ );
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createAchievementMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroupsachievement-%d', $achievement['id']),
+ $achievement['title'],
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroupsachievements->setIconForAchievement(
+ $achievement['id'],
+ $mediaId
+ );
+ }
+ }
+
+ // Redirect
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'manage',
+ $seminary['url'],
+ $groupsgroup['url']
+ ), 1, true, null, false, $achievement['url']
+ )
+ );
+ }
+ }
+
+ // Get validation settings
+ $validationSettings = array();
+ foreach($fields as &$field) {
+ $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field];
+ }
+
+
+ // TODO Set title
+ $this->addTitleLocalized('Edit Achievement');
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('achievement', $achievement);
+ $this->set('title', $title);
+ $this->set('description', $description);
+ $this->set('mimetypes', $mimetypes);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete an Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $groupsgroupUrl URL-title of Character groups-group
+ * @param string $achievementUrl URL-title of Achievement
+ */
+ public function delete($seminaryUrl, $groupsgroupUrl, $achievementUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByUrl($groupsgroup['id'], $achievementUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Charactergroupsachievements->deleteAchievement($achievement);
+ }
+
+ // Redirect to entry
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'manage',
+ $seminary['url'],
+ $groupsgroup['url']
+ ),
+ 1
+ )
+ );
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete seminary');
+ $this->addTitle($groupsgroup['name']);
+ $this->addTitle($seminary['title']);
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('achievement', $achievement);
+ }
+ }
+
+?>
diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc
new file mode 100644
index 00000000..332a0dc6
--- /dev/null
+++ b/controllers/CharactergroupsquestsController.inc
@@ -0,0 +1,720 @@
+
+ * @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', 'charactergroupsqueststations', '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 group(s)
+ $stationgroups = null;
+ $stationgroup = null;
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) {
+ $stationgroups = $this->Charactergroups->getGroupsForGroupsgroup($groupsgroup['id']);
+ }
+ else
+ {
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+ $stationgroups = $this->Charactergroups->getGroupsForCharacter($character['id']);
+ if(!empty($stationgroups)) {
+ $stationgroup = $stationgroups[0];
+ }
+ }
+ // Select group by parameter
+ $selectedStationGroupId = $this->request->getGetParam('stationgroup');
+ if(!is_null($selectedStationGroupId))
+ {
+ $selectedStationGroupId = intval($selectedStationGroupId);
+ foreach($stationgroups as &$group) {
+ if($group['id'] == $selectedStationGroupId) {
+ $stationgroup = $group;
+ break;
+ }
+ }
+ }
+
+ // Get Stations
+ $stations = null;
+ $stations = $this->Charactergroupsqueststations->getStationsForQuest($quest['id']);
+ $stationsDiscovered = false;
+ foreach($stations as &$station)
+ {
+ // Icon
+ if(!is_null($station['stationpicture_id'])) {
+ $station['picture'] = $this->Media->getSeminaryMediaById($station['stationpicture_id']);
+ }
+
+ // Entered state
+ if(!is_null($stationgroup)) {
+ $station['entered'] = $this->Charactergroupsqueststations->hasCharactergroupEnteredStation(
+ $station['id'],
+ $stationgroup['id']
+ );
+ if($station['entered']) {
+ $stationsDiscovered = true;
+ $station['tried'] = $this->Charactergroupsqueststations->hasCharactergroupTriedStation(
+ $station['id'],
+ $stationgroup['id']
+ );
+ if($station['tried']) {
+ $station['solved'] = $this->Charactergroupsqueststations->hasCharactergroupSolvedStation(
+ $station['id'],
+ $stationgroup['id']
+ );
+ }
+ }
+ }
+ }
+
+ // Get Character groups
+ $groups = $this->Charactergroups->getGroupsForQuest($quest['id']);
+ foreach($groups as &$group)
+ {
+ // Get count of solved Stations
+ $group['stations'] = $this->Charactergroupsqueststations->getSolvedStationsForQuestAndGroup(
+ $quest['id'],
+ $group['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('stations', $stations);
+ $this->set('stationsDiscovered', $stationsDiscovered);
+ $this->set('stationgroups', $stationgroups);
+ $this->set('stationgroup', $stationgroup);
+ $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']);
+ foreach($groups as &$group)
+ {
+ // Get count of solved Stations
+ $group['stations'] = $this->Charactergroupsqueststations->getSolvedStationsForQuestAndGroup(
+ $quest['id'],
+ $group['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');
+ 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'];
+ $public = $quest['public'];
+ $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');
+ $public = !is_null($this->request->getPostParam('public'));
+
+ // 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,
+ $public
+ );
+ $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('public', $public);
+ $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/CharactergroupsqueststationsController.inc b/controllers/CharactergroupsqueststationsController.inc
new file mode 100644
index 00000000..60c3610b
--- /dev/null
+++ b/controllers/CharactergroupsqueststationsController.inc
@@ -0,0 +1,1233 @@
+
+ * @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 CharactergroupsqueststationAgent to display Character
+ * groups Quest stations.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsqueststationsController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'charactergroups', 'charactergroupsquests', 'charactergroupsqueststations', 'stationtypes', 'media');
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user'),
+ 'station' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator', 'user'),
+ 'edit' => 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', 'user'),
+ 'station' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'edittask' => array('admin', 'moderator'),
+ 'delete' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Show all stations of a Character groups Quest.
+ *
+ * @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 index($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 Character group
+ $charactergroups = null;
+ $charactergroup = null;
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) {
+ $charactergroups = $this->Charactergroups->getGroupsForGroupsgroup($groupsgroup['id']);
+ }
+ else {
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+ $charactergroups = $this->Charactergroups->getGroupsForCharacter($character['id']);
+ if(!empty($charactergroups)) {
+ $charactergroup = $charactergroups[0];
+ }
+ }
+ // Select group by parameter
+ $selectedStationGroupId = $this->request->getGetParam('stationgroup');
+ if(!is_null($selectedStationGroupId))
+ {
+ $selectedStationGroupId = intval($selectedStationGroupId);
+ foreach($charactergroups as &$group) {
+ if($group['id'] == $selectedStationGroupId) {
+ $charactergroup = $group;
+ break;
+ }
+ }
+ }
+
+ // Get Stations
+ $stations = array();
+ if(is_null($charactergroup)) {
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) {
+ $stations = $this->Charactergroupsqueststations->getStationsForQuest($quest['id']);
+ }
+ }
+ else {
+ $stations = $this->Charactergroupsqueststations->getEnteredStationsForQuestAndGroup($quest['id'], $charactergroup['id']);
+ }
+
+
+ // Pass data to view
+ $this->set('stations', $stations);
+ $this->set('hasgroup', !is_null($charactergroup));
+ }
+
+
+ /**
+ * Action: station.
+ *
+ * Show a station of a Character groups Quest.
+ *
+ * @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
+ * @param string $stationUrl URL of station
+ */
+ public function station($seminaryUrl, $groupsgroupUrl, $questUrl, $stationUrl)
+ {
+ // 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 Station
+ $station = $this->Charactergroupsqueststations->getStationByUrl($quest['id'], $stationUrl);
+
+ // Get media
+ if(!is_null($station['rightimage_id'])) {
+ $station['rightimage'] = $this->Media->getSeminaryMediaById($station['rightimage_id']);
+ }
+ if(!is_null($station['rightav_id'])) {
+ $station['rightav'] = $this->Media->getSeminaryMediaById($station['rightav_id']);
+ }
+ if(!is_null($station['wrongimage_id'])) {
+ $station['wrongimage'] = $this->Media->getSeminaryMediaById($station['wrongimage_id']);
+ }
+ if(!is_null($station['wrongav_id'])) {
+ $station['wrongav'] = $this->Media->getSeminaryMediaById($station['wrongav_id']);
+ }
+
+ // Get Character group(s)
+ $stationgroups = null;
+ $stationgroup = null;
+ $charactergroup = null;
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) {
+ $stationgroups = $this->Charactergroups->getGroupsForGroupsgroup($groupsgroup['id']);
+ }
+ else
+ {
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+ $stationgroups = $this->Charactergroups->getGroupsForCharacter($character['id']);
+ if(!empty($stationgroups)) {
+ $stationgroup = $stationgroups[0];
+ $charactergroup = $stationgroup;
+ }
+ }
+ // Select group by parameter
+ $selectedStationGroupId = $this->request->getGetParam('stationgroup');
+ if(!is_null($selectedStationGroupId))
+ {
+ $selectedStationGroupId = intval($selectedStationGroupId);
+ foreach($stationgroups as &$group) {
+ if($group['id'] == $selectedStationGroupId) {
+ $stationgroup = $group;
+ break;
+ }
+ }
+ }
+
+ // TODO Check permissions
+
+ // Set status “entered”
+ if(!is_null($charactergroup)) {
+ $this->Charactergroupsqueststations->setStationEntered($station['id'], $charactergroup['id']);
+ }
+
+ // Get Character groups-groups
+ $groups = null;
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0)
+ {
+ $groups = $this->Charactergroups->getGroupsForQueststation($station['id']);
+ foreach($groups as &$group) {
+ $group['tried'] = $this->Charactergroupsqueststations->hasCharactergroupTriedStation(
+ $station['id'],
+ $group['id']
+ );
+ if($group['tried']) {
+ $group['solved'] = $this->Charactergroupsqueststations->hasCharactergroupSolvedStation(
+ $station['id'],
+ $group['id']
+ );
+ }
+ }
+ }
+
+ // Task
+ $task = null;
+ $stationtype = $this->Stationtypes->getStationtypeById($station['stationtype_id']);
+ if(!is_null($stationtype['classname'])) {
+ $task = $this->renderTask($stationtype['classname'], $seminary, $groupsgroup, $quest, $station, $stationgroup);
+ }
+ else
+ {
+ // Mark Station as solved
+ if(!is_null($charactergroup)) {
+ $this->Charactergroupsqueststations->setStationSolved(
+ $station['id'],
+ $charactergroup['id']
+ );
+ }
+ }
+
+ // Status
+ $entered = false;
+ $tried = false;
+ $solved = false;
+ if(!is_null($stationgroup)) {
+ $entered = $this->Charactergroupsqueststations->hasCharactergroupEnteredStation(
+ $station['id'],
+ $stationgroup['id']
+ );
+ if($entered) {
+ $tried = $this->Charactergroupsqueststations->hasCharactergroupTriedStation(
+ $station['id'],
+ $stationgroup['id']
+ );
+ if($tried) {
+ $solved = $this->Charactergroupsqueststations->hasCharactergroupSolvedStation(
+ $station['id'],
+ $stationgroup['id']
+ );
+ }
+ }
+ }
+
+
+ // Set title
+ $this->addTitle($station['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('station', $station);
+ $this->set('task', $task);
+ $this->set('groups', $groups);
+ $this->set('stationgroups', $stationgroups);
+ $this->set('stationgroup', $stationgroup);
+ $this->set('entered', $entered);
+ $this->set('tried', $tried);
+ $this->set('solved', $solved);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Character groups Quest Station for a Character
+ * groups Quest.
+ *
+ * @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 create($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 Quest types
+ $stationtypes = $this->Stationtypes->getStationtypes();
+ foreach($stationtypes as &$stationtype) {
+ $stationtype['selected'] = false;
+ }
+
+ // Get allowed mimetypes
+ $iconMimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+ $imageMimetypes = \nre\configs\AppConfig::$mimetypes['stationimages'];
+ $avMimetypes = \nre\configs\AppConfig::$mimetypes['stationavs'];
+
+ // Values
+ $title = '';
+ $prolog = '';
+ $task = '';
+ $longitude = null;
+ $latitude = null;
+ $rightText = '';
+ $wrongText = '';
+ $textMedia = array(
+ 'rightimage' => null,
+ 'rightav' => null,
+ 'wrongimage' => null,
+ 'wrongav' => null
+ );
+ $fields = array('title');
+ $validation = array();
+
+ // Create a new Station
+ 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->Charactergroupsqueststations->stationTitleExists($quest['id'], $title)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $prolog = $this->request->getPostParam('prolog');
+ $task = $this->request->getPostParam('task');
+ $longitude = $this->request->getPostParam('longitude');
+ $latitude = $this->request->getPostParam('latitude');
+ $rightText = $this->request->getPostParam('rightText');
+ $wrongText = $this->request->getPostParam('wrongText');
+
+ // Validate Stationtype
+ $stationtypeIndex = null;
+ foreach($stationtypes as $index => &$stationtype)
+ {
+ $stationtype['selected'] = ($stationtype['url'] == $this->request->getPostParam('stationtype'));
+ if($stationtype['selected']) {
+ $stationtypeIndex = $index;
+ }
+ }
+ if(is_null($stationtypeIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($stationtype);
+ }
+
+ // 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($iconMimetypes 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 media
+ if(!empty($_FILES))
+ {
+ foreach(array_keys($textMedia) as $mediumKey)
+ {
+ if(array_key_exists($mediumKey, $_FILES) && $_FILES[$mediumKey]['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $mediumValidation = true;
+ $medium = $_FILES[$mediumKey];
+
+ // Check error
+ if($medium['error'] !== UPLOAD_ERR_OK) {
+ $mediumValidation = $this->Validation->addValidationResults($mediumValidation, 'error', $medium['error']);
+ }
+ else
+ {
+ // Check mimetype
+ $mediaMimetype = null;
+ $medium['mimetype'] = \hhu\z\Utils::getMimetype($medium['tmp_name'], $medium['type']);
+ $mediaMimetypes = (strpos($mediumKey, 'image') !== false) ? $imageMimetypes : $avMimetypes;
+ foreach($mediaMimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $medium['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $mediumValidation = $this->Validation->addValidationResults($mediumValidation, 'mimetype', $medium['mimetype']);
+ }
+ elseif($medium['size'] > $mediaMimetype['size']) {
+ $mediumValidation = $this->Validation->addValidationResults($mediumValidation, 'size', $mediaMimetype['size']);
+ }
+
+ if($validation == true) {
+ $textMedia[$mediumKey] = $medium;
+ }
+ }
+
+ if($mediumValidation !== true) {
+ $validation = $this->Validation->addValidationResults($validation, $mediumKey, $mediumValidation);
+ }
+ }
+ }
+ }
+
+ // Create new Station
+ if($validation === true)
+ {
+ $stationId = $this->Charactergroupsqueststations->createStation(
+ $quest['id'],
+ $stationtypes[$stationtypeIndex]['id'],
+ $title,
+ $prolog,
+ $task,
+ $latitude,
+ $longitude,
+ $rightText,
+ $wrongText
+ );
+ $station = $this->Charactergroupsqueststations->getStationById($stationId);
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroupsqueststation-%s', $station['url']),
+ '',
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroupsqueststations->setPictureForStation($station['id'], $mediaId);
+ }
+ }
+
+ // Upload media
+ $textMediaIds = array();
+ foreach($textMedia as $mediumKey => $medium)
+ {
+ if(!is_null($medium))
+ {
+ $mediaId = $this->Media->createStationMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('station-%d-%s', $station['id'], $mediumKey),
+ $medium['name'],
+ $medium['type'],
+ $medium['tmp_name']
+ );
+ $textMediaIds[$mediumKey] = $mediaId;
+ }
+ }
+
+ // Set media
+ if(array_key_exists('rightimage', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setRightImageForStation(
+ $station['id'],
+ $textMediaIds['rightimage']
+ );
+ }
+ if(array_key_exists('rightav', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setRightAvForStation(
+ $station['id'],
+ $textMediaIds['rightav']
+ );
+ }
+ if(array_key_exists('wrongimage', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setWrongImageForStation(
+ $station['id'],
+ $textMediaIds['wrongimage']
+ );
+ }
+ if(array_key_exists('wrongav', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setWrongAvForStation(
+ $station['id'],
+ $textMediaIds['wrongav']
+ );
+ }
+
+ // Redirect to Station page
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'station',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $quest['url'],
+ $station['url']
+ ),
+ 1
+ )
+ );
+ }
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Create Station');
+ $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('title', $title);
+ $this->set('prolog', $prolog);
+ $this->set('task', $task);
+ $this->set('longitude', $longitude);
+ $this->set('latitude', $latitude);
+ $this->set('righttext', $rightText);
+ $this->set('wrongtext', $wrongText);
+ $this->set('iconMimetypes', $iconMimetypes);
+ $this->set('imageMimetypes', $imageMimetypes);
+ $this->set('avMimetypes', $avMimetypes);
+ $this->set('stationtypes', $stationtypes);
+ $this->set('validation', $validation);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a Station of a Character groups Quest.
+ *
+ * @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
+ * @param string $stationUrl URL of station
+ */
+ public function edit($seminaryUrl, $groupsgroupUrl, $questUrl, $stationUrl)
+ {
+ // 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 Station
+ $station = $this->Charactergroupsqueststations->getStationByUrl($quest['id'], $stationUrl);
+
+ // Get media
+ if(!is_null($station['rightimage_id'])) {
+ $station['rightimage'] = $this->Media->getSeminaryMediaById($station['rightimage_id']);
+ }
+ if(!is_null($station['rightav_id'])) {
+ $station['rightav'] = $this->Media->getSeminaryMediaById($station['rightav_id']);
+ }
+ if(!is_null($station['wrongimage_id'])) {
+ $station['wrongimage'] = $this->Media->getSeminaryMediaById($station['wrongimage_id']);
+ }
+ if(!is_null($station['wrongav_id'])) {
+ $station['wrongav'] = $this->Media->getSeminaryMediaById($station['wrongav_id']);
+ }
+
+ // Get Quest types
+ $stationtypes = $this->Stationtypes->getStationtypes();
+ foreach($stationtypes as &$stationtype) {
+ $stationtype['selected'] = ($stationtype['id'] == $station['stationtype_id']);
+ }
+
+ // Get allowed mimetypes
+ $iconMimetypes = \nre\configs\AppConfig::$mimetypes['icons'];
+ $imageMimetypes = \nre\configs\AppConfig::$mimetypes['stationimages'];
+ $avMimetypes = \nre\configs\AppConfig::$mimetypes['stationavs'];
+
+ // Values
+ $title = $station['title'];
+ $prolog = $station['prolog'];
+ $task = $station['task'];
+ $longitude = $station['longitude'];
+ $latitude = $station['latitude'];
+ $rightText = $station['righttext'];
+ $wrongText = $station['wrongtext'];
+ $textMedia = array(
+ 'rightimage' => null,
+ 'rightav' => null,
+ 'wrongimage' => null,
+ 'wrongav' => null
+ );
+ $fields = array('title');
+ $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->Charactergroupsqueststations->stationTitleExists($quest['id'], $title, $station['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title', 'exist', true);
+ }
+ $prolog = $this->request->getPostParam('prolog');
+ $task = $this->request->getPostParam('task');
+ $longitude = $this->request->getPostParam('longitude');
+ $latitude = $this->request->getPostParam('latitude');
+ $rightText = $this->request->getPostParam('rightText');
+ $wrongText = $this->request->getPostParam('wrongText');
+
+ // Validate Stationtype
+ $stationtypeIndex = null;
+ foreach($stationtypes as $index => &$stationtype)
+ {
+ $stationtype['selected'] = ($stationtype['url'] == $this->request->getPostParam('stationtype'));
+ if($stationtype['selected']) {
+ $stationtypeIndex = $index;
+ }
+ }
+ if(is_null($stationtypeIndex)) {
+ throw new \nre\exceptions\ParamsNotValidException($stationtype);
+ }
+
+ // 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($iconMimetypes 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 media
+ if(!empty($_FILES))
+ {
+ foreach(array_keys($textMedia) as $mediumKey)
+ {
+ if(array_key_exists($mediumKey, $_FILES) && $_FILES[$mediumKey]['error'] != UPLOAD_ERR_NO_FILE)
+ {
+ $mediumValidation = true;
+ $medium = $_FILES[$mediumKey];
+
+ // Check error
+ if($medium['error'] !== UPLOAD_ERR_OK) {
+ $mediumValidation = $this->Validation->addValidationResults($mediumValidation, 'error', $medium['error']);
+ }
+ else
+ {
+ // Check mimetype
+ $mediaMimetype = null;
+ $medium['mimetype'] = \hhu\z\Utils::getMimetype($medium['tmp_name'], $medium['type']);
+ $mediaMimetypes = (strpos($mediumKey, 'image') !== false) ? $imageMimetypes : $avMimetypes;
+ foreach($mediaMimetypes as &$mimetype) {
+ if($mimetype['mimetype'] == $medium['mimetype']) {
+ $mediaMimetype = $mimetype;
+ break;
+ }
+ }
+ if(is_null($mediaMimetype)) {
+ $mediumValidation = $this->Validation->addValidationResults($mediumValidation, 'mimetype', $medium['mimetype']);
+ }
+ elseif($medium['size'] > $mediaMimetype['size']) {
+ $mediumValidation = $this->Validation->addValidationResults($mediumValidation, 'size', $mediaMimetype['size']);
+ }
+
+ if($validation == true) {
+ $textMedia[$mediumKey] = $medium;
+ }
+ }
+
+ if($mediumValidation !== true) {
+ $validation = $this->Validation->addValidationResults($validation, $mediumKey, $mediumValidation);
+ }
+ }
+ }
+ }
+
+ // Edit Station
+ if($validation === true)
+ {
+ $this->Charactergroupsqueststations->editStation(
+ $station['id'],
+ $stationtypes[$stationtypeIndex]['id'],
+ $title,
+ $prolog,
+ $task,
+ $latitude,
+ $longitude,
+ $rightText,
+ $wrongText
+ );
+ $station = $this->Charactergroupsqueststations->getStationById($station['id']);
+
+ // Upload icon
+ if(!is_null($icon))
+ {
+ $mediaId = $this->Media->createQuestMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('charactergroupsqueststation-%s', $station['url']),
+ '',
+ $icon['mimetype'],
+ $icon['tmp_name']
+ );
+ if($mediaId !== false) {
+ $this->Charactergroupsqueststations->setPictureForStation($station['id'], $mediaId);
+ }
+ }
+
+ // Upload media
+ $textMediaIds = array();
+ foreach($textMedia as $mediumKey => $medium)
+ {
+ if(!is_null($medium))
+ {
+ $mediaId = $this->Media->createStationMedia(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ sprintf('station-%d-%s', $station['id'], $mediumKey),
+ $medium['name'],
+ $medium['type'],
+ $medium['tmp_name']
+ );
+ $textMediaIds[$mediumKey] = $mediaId;
+ }
+ }
+
+ // Set media
+ if(array_key_exists('rightimage', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setRightImageForStation(
+ $station['id'],
+ $textMediaIds['rightimage']
+ );
+ }
+ if(array_key_exists('rightav', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setRightAvForStation(
+ $station['id'],
+ $textMediaIds['rightav']
+ );
+ }
+ if(array_key_exists('wrongimage', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setWrongImageForStation(
+ $station['id'],
+ $textMediaIds['wrongimage']
+ );
+ }
+ if(array_key_exists('wrongav', $textMediaIds)) {
+ $this->Charactergroupsqueststations->setWrongAvForStation(
+ $station['id'],
+ $textMediaIds['wrongav']
+ );
+ }
+
+ // Redirect
+ if(!is_null($this->request->getPostParam('edit-task'))) {
+ // To task editing
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'edittask',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $quest['url'],
+ $station['url']
+ ),
+ 1
+ )
+ );
+ }
+ else {
+ // To Station page
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'station',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $quest['url'],
+ $station['url']
+ ),
+ 1
+ )
+ );
+ }
+ }
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Edit Station');
+ $this->addTitle($station['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('station', $station);
+ $this->set('title', $title);
+ $this->set('prolog', $prolog);
+ $this->set('task', $task);
+ $this->set('longitude', $longitude);
+ $this->set('latitude', $latitude);
+ $this->set('righttext', $rightText);
+ $this->set('wrongtext', $wrongText);
+ $this->set('iconMimetypes', $iconMimetypes);
+ $this->set('imageMimetypes', $imageMimetypes);
+ $this->set('avMimetypes', $avMimetypes);
+ $this->set('stationtypes', $stationtypes);
+ $this->set('validation', $validation);
+ }
+
+
+ /**
+ * Action: edittask.
+ *
+ * Edit the task of a Character groups Quest Station.
+ *
+ * @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
+ * @param string $stationUrl URL of station
+ */
+ public function edittask($seminaryUrl, $groupsgroupUrl, $questUrl, $stationUrl)
+ {
+ // 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 Station
+ $station = $this->Charactergroupsqueststations->getStationByUrl($quest['id'], $stationUrl);
+
+ // Render editing task
+ $task = null;
+ $stationtype = $this->Stationtypes->getStationtypeById($station['stationtype_id']);
+ if(!is_null($stationtype['classname'])) {
+ $task = $this->renderTaskEditing($stationtype['classname'], $seminary, $groupsgroup, $quest, $station);
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Edit Station task');
+ $this->addTitle($station['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('task', $task);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Station of a Character groups Quest.
+ *
+ * @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
+ * @param string $stationUrl URL of station
+ */
+ public function delete($seminaryUrl, $groupsgroupUrl, $questUrl, $stationUrl)
+ {
+ // 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 Station
+ $station = $this->Charactergroupsqueststations->getStationByUrl($quest['id'], $stationUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete seminary
+ $this->Charactergroupsqueststations->deleteStation(
+ $station['id']
+ );
+
+ // Redirect to overview
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'charactergroupsquests',
+ 'quest',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $quest['url']
+ ),
+ 0, true, null, true, 'stations'
+ )
+ );
+ }
+
+ // Redirect to entry
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'station',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $quest['url'],
+ $station['url']
+ ),
+ 1
+ )
+ );
+ }
+
+
+ // Set title
+ $this->addTitleLocalized('Delete Station');
+ $this->addTitle($station['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('station', $station);
+ }
+
+
+
+
+ /**
+ * Render and handle the task of a Station.
+ *
+ * @param string $stationtypeClassname Name of the class for the Stationtype of a Station
+ * @param array $seminary Seminary data
+ * @param array $questgroup Questgroup data
+ * @param array $quest Quest data
+ * @param array $station Station data
+ * @param array $charactergroup Charactergroup data
+ * @return string Rendered output
+ */
+ private function renderTask($stationtypeClassname, $seminary, $groupsgroup, $quest, $station, $charactergroup)
+ {
+ $task = null;
+ try {
+ // Generate request and response
+ $request = clone $this->request;
+ $response = $this->createStationtypeResponse('quest', $seminary, $groupsgroup, $quest, $station, $charactergroup);
+
+ // Load Stationtype Agent
+ $stationtypeAgent = $this->loadStationtypeAgent($stationtypeClassname, $request, $response);
+
+ // Solve Quest
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit')))
+ {
+ // Get user answers
+ $answer = $this->request->getPostParam('answer');
+
+ // Save answers in database
+ try {
+ // save answer
+ if(!is_null($charactergroup)) {
+ if(!$this->Charactergroupsqueststations->hasCharactergroupSolvedStation($station['id'], $charactergroup['id'])) {
+ $stationtypeAgent->saveAnswer($seminary, $groupsgroup, $quest, $station, $charactergroup, $answer);
+ }
+ }
+
+ // Match answer with correct one
+ $status = $stationtypeAgent->matchAnswer(
+ $seminary,
+ $groupsgroup,
+ $quest,
+ $station,
+ $charactergroup,
+ $answer
+ );
+ if($status === true)
+ {
+ if(!is_null($charactergroup))
+ {
+ // Mark Station as solved
+ $this->Charactergroupsqueststations->setStationSolved($station['id'], $charactergroup['id']);
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array(), 6, true, null, false, 'task'));
+ }
+ elseif($status === false)
+ {
+ if(!is_null($charactergroup)) {
+ // Mark Station as unsolved
+ $this->Charactergroupsqueststations->setStationUnsolved(
+ $station['id'],
+ $charactergroup['id']
+ );
+ }
+
+ // Redirect
+ $this->redirect($this->linker->link(array(), 6, true, null, false, 'task'));
+ }
+ else {
+ // Redirect
+ $this->redirect($this->linker->link(array(), 6, true, null, false, 'task'));
+ }
+ }
+ catch(\hhu\z\exceptions\SubmissionNotValidException $e) {
+ $response->addParam($e);
+ }
+ }
+
+ // Render Task
+ $task = $this->runStationtypeAgent($stationtypeAgent, $request, $response);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeModelNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeModelNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeControllerNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeControllerNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeAgentNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\stationtypeAgentNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+
+
+ // Return rendered output
+ return $task;
+ }
+
+
+ /**
+ * Render editing of a Station task.
+ *
+ * @param string $stationtypeClassname Name of the class for the Stationtype of a Station
+ * @param array $seminary Seminary data
+ * @param array $groupsgroup Groupsgroup data
+ * @param array $quest Quest data
+ * @param array $station Station data
+ * @return string Rendered output
+ */
+ private function renderTaskEditing($stationtypeClassname, $seminary, $groupsgroup, $quest, $station)
+ {
+ $task = null;
+ try {
+ // Generate request and response
+ $request = clone $this->request;
+ $response = $this->createStationtypeResponse('edittask', $seminary, $groupsgroup, $quest, $station);
+
+ // Load Stationtype Agent
+ $stationtypeAgent = $this->loadStationtypeAgent($stationtypeClassname, $request, $response);
+
+ // Render Task
+ $task = $this->runStationtypeAgent($stationtypeAgent, $request, $response);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeModelNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeModelNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeControllerNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeControllerNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\StationtypeAgentNotValidException $e) {
+ $task = $e->getMessage();
+ }
+ catch(\hhu\z\exceptions\stationtypeAgentNotFoundException $e) {
+ $task = $e->getMessage();
+ }
+
+
+ // Return rendered output
+ return $task;
+ }
+
+
+ /**
+ * Create a response for the Stationtype 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 createStationtypeResponse($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 StationtypeAgent for a Stationtype.
+ *
+ * @param string $stationtypeClassname Name of the class for the Stationtype of a Station
+ * @param \nre\core\Request $request Request
+ * @param \nre\core\Response $response Response
+ * @return \hhu\z\agents\StationtypeAgent Stationtype Agent
+ */
+ private function loadStationtypeAgent($stationtypeClassname, $request, $response)
+ {
+ // Load Agent
+ \hhu\z\agents\StationtypeAgent::load($stationtypeClassname);
+
+
+ // Construct and return Agent
+ return \hhu\z\agents\StationtypeAgent::factory($stationtypeClassname, $request, $response);
+ }
+
+
+ /**
+ * Run and render the Agent for a StationtypeAgent and return ist output.
+ *
+ * @param \nre\core\Agent $stationtypeAgent StationtypeAgent to run and render
+ * @param \nre\core\Request $request Request
+ * @param \nre\core\Response $response Response
+ * @return string Rendered output
+ */
+ private function runStationtypeAgent($stationtypeAgent, $request, $response)
+ {
+ // Run Agent
+ $stationtypeAgent->run($request, $response);
+
+
+ // Render and return output
+ return $stationtypeAgent->render();
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc
new file mode 100644
index 00000000..edc9b935
--- /dev/null
+++ b/controllers/CharactersController.inc
@@ -0,0 +1,779 @@
+
+ * @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', 'charactertitles', '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']);
+ }
+ if(!is_null($character['charactertitle_id']) && !is_null($character['gender']))
+ {
+ $title = $this->Charactertitles->getTitleById($character['charactertitle_id']);
+ $character['title'] = $title[($character['gender']) ? 'title_male' : 'title_female'];
+ }
+
+ // 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'])
+ );
+ foreach($ranking['superior'] as &$c)
+ {
+ if(!is_null($c['charactertitle_id']) && !is_null($c['gender']))
+ {
+ $title = $this->Charactertitles->getTitleById($c['charactertitle_id']);
+ $c['title'] = $title[($c['gender']) ? 'title_male' : 'title_female'];
+ }
+ }
+ foreach($ranking['inferior'] as &$c)
+ {
+ if(!is_null($c['charactertitle_id']) && !is_null($c['gender']))
+ {
+ $title = $this->Charactertitles->getTitleById($c['charactertitle_id']);
+ $c['title'] = $title[($c['gender']) ? 'title_male' : 'title_female'];
+ }
+ }
+
+ // 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 = '';
+ $gender = null;
+ $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 gender
+ if(is_null($gender) || !in_array(intval($gender), array(0, 1))) {
+ $validation = $this->Validation->addValidationResult($validation, 'gender', 'correct', 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,
+ $gender
+ );
+ $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 titles
+ $titles = $this->Charactertitles->getTitlesForCharacter(
+ $character['id']
+ );
+
+ // 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'];
+ $gender = $character['gender'];
+ $charactertitle = null;
+ if(!is_null($character['charactertitle_id'])) {
+ foreach($titles as &$title) {
+ if($title['id'] == $character['charactertitle_id']) {
+ $charactertitle = $title;
+ }
+ }
+ }
+ $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);
+ }
+ $gender = $this->request->getPostParam('gender');
+ $charactertitleId = $this->request->getPostParam('title');
+
+ // 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 gender
+ if(is_null($gender) || ($gender !== "0" && $gender !== "1")) {
+ $validation = $this->Validation->addValidationResult($validation, 'gender', 'correct', false);
+ }
+
+ // Validate title
+ $charactertitle = null;
+ if(!is_null($charactertitleId))
+ {
+ $charatcretitle = null;
+ $charactertitleId = intval($charactertitleId);
+ foreach($titles as &$title) {
+ if($title['id'] === $charactertitleId) {
+ $charactertitle = $title;
+ break;
+ }
+ }
+ }
+
+ // 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,
+ intval($gender),
+ (!is_null($charactertitle)) ? $charactertitle['id'] : null
+ );
+
+ // 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('titles', $titles);
+ $this->set('fields', $fields);
+ $this->set('charactername', $charactername);
+ $this->set('gender', $gender);
+ $this->set('charactertitle', $charactertitle);
+ $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/CharactertitlesController.inc b/controllers/CharactertitlesController.inc
new file mode 100644
index 00000000..bcb36efe
--- /dev/null
+++ b/controllers/CharactertitlesController.inc
@@ -0,0 +1,280 @@
+
+ * @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 CharactertitlesAgent to handle Character titles of a
+ * Seminary.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactertitlesController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('validation');
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('charactertitles');
+ /**
+ * 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')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator'),
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'delete' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List Character titles.
+ *
+ * @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 titles
+ $titles = $this->Charactertitles->getTitlesForSeminary($seminary['id']);
+
+
+ // Set titile
+ $this->addTitleLocalized('Charactertitles');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('titles', $titles);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new Character title 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);
+
+ // Values
+ $titleMale = '';
+ $titleFemale = '';
+ $fields = array('title_male', 'title_female');
+ $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);
+ $titleMale = $this->request->getPostParam('title_male');
+ if($this->Charactertitles->titleExists($seminary['id'], $titleMale)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title_male', 'exist', true);
+ }
+ $titleFemale = $this->request->getPostParam('title_female');
+ if($this->Charactertitles->titleExists($seminary['id'], $titleFemale)) {
+ $validation = $this->Validation->addValidationResult($validation, 'title_female', 'exist', true);
+ }
+
+ // Create new Character title
+ if($validation === true)
+ {
+ $titleId = $this->Charactertitles->createTitle(
+ $this->Auth->getUserId(),
+ $seminary['id'],
+ $titleMale,
+ $titleFemale
+ );
+ $title = $this->Charactertitles->getTitleById($titleId);
+
+ // 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('Create new Charactertitle');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('titleMale', $titleMale);
+ $this->set('titleFemale', $titleFemale);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a Character title for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of a Seminary
+ * @param string $titleHash Hash of Character title
+ */
+ public function edit($seminaryUrl, $titleHash)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character title
+ $title = $this->Charactertitles->getTitleByHash($titleHash);
+
+ // Values
+ $titleMale = $title['title_male'];
+ $titleFemale = $title['title_female'];
+ $fields = array('title_male', 'title_female');
+ $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);
+ $titleMale = $this->request->getPostParam('title_male');
+ if($this->Charactertitles->titleExists($seminary['id'], $titleMale, $title['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title_male', 'exist', true);
+ }
+ $titleFemale = $this->request->getPostParam('title_female');
+ if($this->Charactertitles->titleExists($seminary['id'], $titleFemale, $title['id'])) {
+ $validation = $this->Validation->addValidationResult($validation, 'title_female', 'exist', true);
+ }
+
+ // Edit Character title
+ if($validation === true)
+ {
+ $this->Charactertitles->editTitle(
+ $title['id'],
+ $titleMale,
+ $titleFemale
+ );
+ $title = $this->Charactertitles->getTitleById($title['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 Charactertitle');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('titleMale', $titleMale);
+ $this->set('titleFemale', $titleFemale);
+ $this->set('validation', $validation);
+ $this->set('validationSettings', $validationSettings);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a Character title for a Seminary.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of a Seminary
+ * @param string $titleHash Hash of Character title
+ */
+ public function delete($seminaryUrl, $titleHash)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character title
+ $title = $this->Charactertitles->getTitleByHash($titleHash);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Check confirmation
+ if(!is_null($this->request->getPostParam('delete')))
+ {
+ // Delete Character title
+ $this->Charactertitles->deleteTitle($title['id']);
+ }
+
+ // Redirect to overview
+ $this->redirect($this->linker->link(array('index', $seminary['url']), 1));
+ }
+
+
+ // Set titile
+ $this->addTitleLocalized('Delete Charactertitle');
+ $this->addTitle($seminary['title']);
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('title', $title);
+ }
+
+ }
+
+?>
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..f80fb5c7
--- /dev/null
+++ b/controllers/MediaController.inc
@@ -0,0 +1,737 @@
+
+ * @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'),
+ 'charactergroupsqueststation' => array('admin', 'moderator', 'user'),
+ 'charactergroupsachievements' => 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'),
+ 'charactergroupsqueststation' => array('admin', 'moderator', 'user', 'guest'),
+ 'charactergroupsachievements' => array('admin', 'moderator', 'user', 'guest')
+ );
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'achievements', 'media', 'avatars', 'charactergroups', 'charactergroupsquests', 'charactergroupsqueststations', 'charactergroupsachievements', '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)
+ {
+ // Check action
+ if(!is_null($action) && !array_key_exists($action, \nre\configs\AppConfig::$media)) {
+ throw new \nre\exceptions\ParamsNotValidException($action);
+ }
+
+ // Get Media
+ $media = $this->Media->getMediaByUrl($mediaUrl);
+
+ // Get file
+ $file = $this->getMediaFile(
+ $media,
+ (!is_null($action)) ? \nre\configs\AppConfig::$media[$action] : false
+ );
+ 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)
+ {
+ // Check action
+ if(!is_null($action) && !array_key_exists($action, \nre\configs\AppConfig::$media)) {
+ throw new \nre\exceptions\ParamsNotValidException($action);
+ }
+
+ // 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,
+ (!is_null($action)) ? \nre\configs\AppConfig::$media[$action] : false
+ );
+ 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)
+ {
+ // Check action
+ if(!is_null($action) && !array_key_exists($action, \nre\configs\AppConfig::$media)) {
+ throw new \nre\exceptions\ParamsNotValidException($action);
+ }
+
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Media
+ $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl);
+
+ // Get file
+ $file = $this->getMediaFile(
+ $media,
+ (!is_null($action)) ? \nre\configs\AppConfig::$media[$action] : false
+ );
+ 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,
+ \nre\configs\AppConfig::$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);
+ 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,
+ \nre\configs\AppConfig::$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,
+ \nre\configs\AppConfig::$media['charactergroupsquest']
+ );
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactergroupsqueststation
+ *
+ * Display the icon for a Character groups Quest Station.
+ *
+ * @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
+ * @param string $stationUrl URL-title of Station
+ */
+ public function charactergroupsqueststation($seminaryUrl, $groupsgroupUrl, $questUrl, $stationUrl)
+ {
+ // 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);
+
+ // Get Station
+ $station = $this->Charactergroupsqueststations->getStationByUrl($quest['id'], $stationUrl);
+
+ // Get Character group(s)
+ $stationgroups = null;
+ $stationgroup = null;
+ if(count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0) {
+ $stationgroups = $this->Charactergroups->getGroupsForGroupsgroup($groupsgroup['id']);
+ }
+ else
+ {
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+ $stationgroups = $this->Charactergroups->getGroupsForCharacter($character['id']);
+ if(!empty($stationgroups)) {
+ $stationgroup = $stationgroups[0];
+ }
+ }
+ // Select group by parameter
+ $selectedStationGroupId = $this->request->getGetParam('stationgroup');
+ if(!is_null($selectedStationGroupId))
+ {
+ $selectedStationGroupId = intval($selectedStationGroupId);
+ foreach($stationgroups as &$group) {
+ if($group['id'] == $selectedStationGroupId) {
+ $stationgroup = $group;
+ break;
+ }
+ }
+ }
+
+ // Status
+ $solved = (count(array_intersect(array('admin', 'moderator'), \hhu\z\controllers\SeminaryController::$character['characterroles'])) > 0);
+ if(!is_null($stationgroup)) {
+ $solved = $this->Charactergroupsqueststations->hasCharactergroupSolvedStation(
+ $station['id'],
+ $stationgroup['id']
+ );
+ }
+
+ // Get media
+ $media = $this->Media->getSeminaryMediaById($station['stationpicture_id']);
+
+ // Get file
+ $file = $this->getMediaFile(
+ $media,
+ \nre\configs\AppConfig::$media['charactergroupsqueststation'],
+ !$solved
+ );
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('media', $media);
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactergroupsachievement
+ *
+ * Display the achievement of a Character groups-group.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-title of Seminary
+ * @param string $groupsgroupUrl URL-title of Character groups-group
+ * @param string $achievementUrl URL-title of the Achievement
+ * @param string $action Action for processing the media
+ */
+ public function charactergroupsachievement($seminaryUrl, $groupsgroupUrl, $achievementUrl, $locked=null)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Character
+ //$character = SeminaryController::$character;
+
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByUrl($groupsgroup['id'], $achievementUrl);
+
+ // Get media
+ $media = $this->Media->getSeminaryMediaById($achievement['achievementsmedia_id']);
+
+ // Get file
+ $file = $this->getMediaFile(
+ $media,
+ false,
+ !is_null($locked)
+ );
+ 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 mixed $resize Size to resize image to
+ * @param boolean $grayscale Whether to grayscale image or not
+ * @return object File for the medium (or null if medium is cached)
+ */
+ private function getMediaFile($media, $resize=false, $grayscale=false)
+ {
+ // 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;
+ }
+
+ // Determine processing
+ $resize = (is_array($resize)) ? $resize : false;
+ $grayscale = ($grayscale === true);
+
+ // Load image without processing
+ if((!$grayscale && $resize === false) || !in_array(strtoupper($format), self::getImageTypes()))
+ {
+ // Do not process the file
+ return file_get_contents($media['filename']);
+ }
+
+ // Read image from cache
+ $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($media['filename']).'-'.$resize['width'].'x'.$resize['height'].'-'.(int) $grayscale;
+ if(file_exists($tempFileName))
+ {
+ // Check age of file
+ if(filemtime($media['filename']) > filemtime($tempFileName)) {
+ // Too old, delete
+ unlink($tempFileName);
+ }
+ else {
+ // Valid, read and return
+ return file_get_contents($tempFileName);
+ }
+ }
+
+ // Load file with ImageMagick
+ $file = new \Imagick($media['filename']);
+
+ // Apply grayscale
+ if($grayscale) {
+ self::grayscaleImage($file);
+ }
+
+ // Risize image
+ if($resize !== false) {
+ self::resizeImage($file, $resize['width'], $resize['height']);
+ }
+
+ // Save temporary file
+ $file->setImageFormat($format);
+ $file->writeImage($tempFileName);
+
+
+ // 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 \Imagick $im Image object
+ * @param int $width Max. width to resize to
+ * @param int $height Max. height to resize to
+ */
+ private static function resizeImage($im, $width, $height)
+ {
+ // 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);
+ }
+
+
+ /**
+ * Turn the colors of an image into grayscale.
+ *
+ * @param \Imagick $im Image object
+ */
+ private static function grayscaleImage($im)
+ {
+ $im->modulateImage(100, 0, 100);
+ }
+
+ }
+
+?>
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..757379a3
--- /dev/null
+++ b/controllers/PagesController.inc
@@ -0,0 +1,236 @@
+
+ * @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');
+
+ // 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/QrController.inc b/controllers/QrController.inc
new file mode 100644
index 00000000..c1e0e63b
--- /dev/null
+++ b/controllers/QrController.inc
@@ -0,0 +1,241 @@
+
+ * @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 QrAgent to redirect to a page from a (short) QR-code
+ * link.
+ *
+ * @author Oliver Hanraths
+ */
+ class QrController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'achievements', 'charactertitles', 'charactergroups', 'charactergroupsquests', 'charactergroupsqueststations', 'charactergroupsachievements');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'a' => array('admin', 'moderator', 'user'),
+ 'ct' => array('admin', 'moderator', 'user'),
+ 'cgqs' => array('admin', 'moderator', 'user'),
+ 'cga' => array('admin', 'moderator', 'user')
+ );
+
+
+
+
+ /**
+ * Action: a.
+ *
+ * Trigger an Achievement by a hash typically provided via a QR-code.
+ *
+ * @param $achievementConditionHash Hash value of QR-code condition of an Achievement
+ */
+ public function a($achievementConditionHash)
+ {
+ // Check Achievement condition
+ $condition = $this->Achievements->getAchievementConditionQrcode(
+ $achievementConditionHash
+ );
+ if(empty($condition)) {
+ throw new \nre\exceptions\IdNotFoundException($achievementConditionHash);
+ }
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementById($condition['achievement_id']);
+
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryById($achievement['seminary_id']);
+
+ // Get Character
+ $character = $this->Characters->getCharacterForUserAndSeminary(
+ self::$user['id'],
+ $seminary['id']
+ );
+
+ // Set Achievement achieved
+ $this->Achievements->setAchievementAchieved(
+ $achievement['id'],
+ $character['id']
+ );
+
+ // Add notification
+ $this->Notification->addNotification(
+ \hhu\z\controllers\components\NotificationComponent::TYPE_ACHIEVEMENT,
+ $achievement['title'],
+ $this->linker->link(
+ array(
+ 'achievements',
+ 'index',
+ $seminary['url']
+ ), 0, true, null, true, $achievement['url']
+ ),
+ (
+ !is_null($achievement['achieved_achievementsmedia_id'])
+ ? $this->linker->link(
+ array(
+ 'media',
+ 'achievement',
+ $seminary['url'],
+ $achievement['url']
+ )
+ )
+ : null
+ )
+ );
+
+ // Redirect to Character profile
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'achievements',
+ 'index',
+ $seminary['url']
+ )
+ )
+ );
+ }
+
+
+ /**
+ * Action: ct.
+ *
+ * Trigger a Character title by a hash typically provided via a QR-code.
+ *
+ * @param $titleHash Hash value of Character title
+ */
+ public function ct($titleHash)
+ {
+ // Get Character title
+ $title = $this->Charactertitles->getTitleByHash($titleHash);
+
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryById($title['seminary_id']);
+
+ // Get Character
+ $character = $this->Characters->getCharacterForUserAndSeminary(
+ self::$user['id'],
+ $seminary['id']
+ );
+
+ // Assign title to Character
+ $this->Charactertitles->assignTitleToCharacter(
+ $title['id'],
+ $character['id']
+ );
+
+ // Add notification
+ $this->Notification->addNotification(
+ \hhu\z\controllers\components\NotificationComponent::TYPE_CHARACTERTITLE,
+ ($character['gender'] === 1)
+ ? $title['title_male']
+ : $title['title_female'],
+ $this->linker->link(
+ array(
+ 'characters',
+ 'edit',
+ $seminary['url'],
+ $character['url']
+ )
+ )
+ );
+
+ // Redirect
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'characters',
+ 'character',
+ $seminary['url'],
+ $character['url']
+ )
+ )
+ );
+ }
+
+
+ /**
+ * Action: cgqs.
+ *
+ * Redirect to a Character groups Quest Station.
+ *
+ * @param string $stationHash Hash of Character groups Quest Station
+ */
+ public function cgqs($stationHash)
+ {
+ // Get station
+ $station = $this->Charactergroupsqueststations->getStationByHash($stationHash);
+
+ // Get Character groups Quests
+ $quest = $this->Charactergroupsquests->getQuestById($station['charactergroupsquest_id']);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupById($quest['charactergroupsgroup_id']);
+
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryById($groupsgroup['seminary_id']);
+
+ // Redirect
+ $this->redirect($this->linker->link(array(
+ 'charactergroupsqueststations',
+ 'station',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $quest['url'],
+ $station['url']
+ )));
+ }
+
+
+ /**
+ * Action: cga.
+ *
+ * Redirect to a Character groups Achievements
+ *
+ * @param string $achievementHash Hash of Character groups Achievement
+ */
+ public function cga($achievementHash)
+ {
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByHash($achievementHash);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupById($achievement['charactergroupsgroup_id']);
+
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryById($groupsgroup['seminary_id']);
+
+ // Redirect
+ $this->redirect(
+ $this->linker->link(
+ array(
+ 'charactergroupsachievements',
+ 'achievement',
+ $seminary['url'],
+ $groupsgroup['url'],
+ $achievement['hash']
+ )
+ )
+ );
+ }
+
+ }
+
+?>
diff --git a/controllers/QrcodesController.inc b/controllers/QrcodesController.inc
new file mode 100644
index 00000000..fd904a1e
--- /dev/null
+++ b/controllers/QrcodesController.inc
@@ -0,0 +1,379 @@
+
+ * @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 QrcodeAgent to generate and show QR-codes.
+ *
+ * @author Oliver Hanraths
+ */
+ class QrcodesController extends \hhu\z\controllers\SeminaryController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'achievements', 'charactertitles', 'charactergroups', 'charactergroupsquests', 'charactergroupsqueststations', 'charactergroupsachievements');
+
+
+
+
+ /**
+ * 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: achievement
+ *
+ * Display a QR-code for an Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $achievementUrl URL of Achievement
+ */
+ public function achievement($seminaryUrl, $achievementUrl, $size=1)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Achievement
+ $achievement = $this->Achievements->getAchievementByUrl(
+ $seminary['id'],
+ $achievementUrl
+ );
+
+ // Get condition
+ $conditions = $this->Achievements->getAchievementConditionsQrcode(
+ $achievement['id']
+ );
+ if(empty($conditions)) {
+ throw new IdNotFoundException($achievementUrl);
+ }
+
+ // Generate QR-code
+ $url = $this->linker->link(array('qr', 'a', $conditions[0]['hash']), 0, true, null, true, null, true);
+ $file = $this->generateQRcode($url, $size);
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactertitles
+ *
+ * Display a QR-code for a Character title.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $titleHash Hash of Character title
+ * @param int $size Size of QR-code (default: 1)
+ */
+ public function charactertitle($seminaryUrl, $titleHash, $size=1)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character title
+ $title = $this->Charactertitles->getTitleByHash($titleHash);
+
+ // Generate QR-code
+ $url = $this->linker->link(array('qr', 'ct', $title['hash']), 0, true, null, true, null, true);
+ $file = $this->generateQRcode($url, $size);
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactergroupsqueststation
+ *
+ * Display a QR-code for a Character groups Quest station.
+ *
+ * @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
+ * @param string $stationUrl URL of Character groups Quest station
+ */
+ public function charactergroupsqueststation($seminaryUrl, $groupsgroupUrl, $questUrl, $stationUrl, $size=1)
+ {
+ // 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);
+
+ // Get station
+ $station = $this->Charactergroupsqueststations->getStationByUrl($quest['id'], $stationUrl);
+
+ // Generate QR-code
+ $url = $this->linker->link(array('qr', 'cgqs', $station['url']), 0, true, null, true, null, true);
+ $file = $this->generateQRcode($url, $size);
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $this->set('file', $file);
+ }
+
+
+ /**
+ * Action: charactergroupsachievements
+ *
+ * Display a QR-code for a Character groups Achievement.
+ *
+ * @throws \nre\exceptions\IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $groupsgroupUrl URL-Title of a Character groups-group
+ * @param string $achievementUrl URL of Achievement
+ */
+ public function charactergroupsachievements($seminaryUrl, $groupsgroupUrl, $achievementUrl, $size=1)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Character groups-group
+ $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl);
+
+ // Get Achievement
+ $achievement = $this->Charactergroupsachievements->getAchievementByUrl(
+ $groupsgroup['id'],
+ $achievementUrl
+ );
+
+ // Generate QR-code
+ $url = $this->linker->link(array('qr', 'cga', $achievement['hash']), 0, true, null, true, null, true);
+ $file = $this->generateQRcode($url, $size);
+ if(is_null($file)) {
+ return;
+ }
+
+
+ // Pass data to view
+ $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;
+ }
+
+
+ private function generateQRcode($text, $size)
+ {
+ \hhu\z\lib\Phpqrcode::load();
+ \QRcode::png($text, false, QR_ECLEVEL_L, intval($size), 1);
+
+
+ return null;
+ }
+
+
+ /**
+ * 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/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..eacc9073
--- /dev/null
+++ b/controllers/SeminariesController.inc
@@ -0,0 +1,612 @@
+
+ * @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'];
+ $latitude = $seminary['latitude'];
+ $longitude = $seminary['longitude'];
+ $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');
+ $latitude = $this->request->getPostParam('latitude');
+ $longitude = $this->request->getPostParam('longitude');
+
+ // 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,
+ $latitude,
+ $longitude
+ );
+ $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('latitude', $latitude);
+ $this->set('longitude', $longitude);
+ $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('charactergroupsqueststations', $elements),
+ array_key_exists('charactergroupsachievements', $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..ba22a73d
--- /dev/null
+++ b/controllers/SeminarybarController.inc
@@ -0,0 +1,102 @@
+
+ * @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', 'charactertitles', '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']);
+ if(!is_null($character['charactertitle_id']) && !is_null($character['gender']))
+ {
+ $title = $this->Charactertitles->getTitleById($character['charactertitle_id']);
+ $character['title'] = $title[($character['gender']) ? 'title_male' : 'title_female'];
+ }
+
+ // 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']);
+ foreach($group['members'] as &$member)
+ {
+ if(!is_null($member['charactertitle_id']) && !is_null($member['gender']))
+ {
+ $title = $this->Charactertitles->getTitleById($member['charactertitle_id']);
+ $member['title'] = $title[($member['gender']) ? 'title_male' : 'title_female'];
+ }
+ }
+ $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..19ceb6dd
--- /dev/null
+++ b/controllers/components/NotificationComponent.inc
@@ -0,0 +1,120 @@
+
+ * @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: Character groups Achievement
+ *
+ * @var string
+ */
+ const TYPE_CHARACTERGROUPSACHIEVEMENT = 'charactergroupsachievement';
+ /**
+ * Type: Character title
+ *
+ * @var string
+ */
+ const TYPE_CHARACTERTITLE = 'charactertitle';
+ /**
+ * 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..d695be29
--- /dev/null
+++ b/db/create.sql
@@ -0,0 +1,2666 @@
+-- MySQL dump 10.16 Distrib 10.1.13-MariaDB, for Linux (x86_64)
+--
+-- Host: localhost Database: z
+-- ------------------------------------------------------
+-- Server version 10.1.13-MariaDB
+
+/*!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_qrcode`
+--
+
+DROP TABLE IF EXISTS `achievementconditions_qrcode`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `achievementconditions_qrcode` (
+ `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,
+ `hash` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `hash` (`hash`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `achievement_id` (`achievement_id`),
+ CONSTRAINT `achievementconditions_qrcode_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `achievementconditions_qrcode_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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 `charactergroupsachievements`
+--
+
+DROP TABLE IF EXISTS `charactergroupsachievements`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsachievements` (
+ `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,
+ `pos` int(11) unsigned NOT NULL,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `hash` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `description` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `achievementsmedia_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `charactergroupsgroup_id_2` (`charactergroupsgroup_id`,`url`),
+ UNIQUE KEY `hash` (`hash`),
+ KEY `charactergroupsgroup_id` (`charactergroupsgroup_id`),
+ KEY `achievementsmedia_id` (`achievementsmedia_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `charactergroupsachievements_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `charactergroupsachievements_ibfk_2` FOREIGN KEY (`charactergroupsgroup_id`) REFERENCES `charactergroupsgroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsachievements_ibfk_3` FOREIGN KEY (`achievementsmedia_id`) REFERENCES `achievementsmedia` (`seminarymedia_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 `charactergroupsachievements_charactergroups`
+--
+
+DROP TABLE IF EXISTS `charactergroupsachievements_charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsachievements_charactergroups` (
+ `charactergroupsachievement_id` int(11) NOT NULL,
+ `charactergroup_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`charactergroupsachievement_id`,`charactergroup_id`),
+ KEY `charactergroup_id` (`charactergroup_id`),
+ CONSTRAINT `charactergroupsachievements_charactergroups_ibfk_1` FOREIGN KEY (`charactergroupsachievement_id`) REFERENCES `charactergroupsachievements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsachievements_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 */;
+
+--
+-- 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,
+ `public` tinyint(1) NOT NULL DEFAULT '1',
+ 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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 `charactergroupsqueststations`
+--
+
+DROP TABLE IF EXISTS `charactergroupsqueststations`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsqueststations` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `charactergroupsquest_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `stationtype_id` int(11) NOT NULL,
+ `title` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `url` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `pos` int(10) unsigned NOT NULL,
+ `stationpicture_id` int(11) DEFAULT NULL,
+ `prolog` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `task` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `latitude` decimal(10,6) DEFAULT NULL,
+ `longitude` decimal(10,6) DEFAULT NULL,
+ `righttext` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `wrongtext` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `rightimage_id` int(11) DEFAULT NULL,
+ `rightav_id` int(11) DEFAULT NULL,
+ `wrongimage_id` int(11) DEFAULT NULL,
+ `wrongav_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `charactergroupsquest_id_2` (`charactergroupsquest_id`,`url`),
+ UNIQUE KEY `charactergroupsquest_id_3` (`charactergroupsquest_id`,`pos`),
+ KEY `charactergroupsquest_id` (`charactergroupsquest_id`),
+ KEY `charactergroupsqueststationtype_id` (`stationtype_id`),
+ KEY `stationpicture_id` (`stationpicture_id`) USING BTREE,
+ KEY `rightimage_id` (`rightimage_id`),
+ KEY `rightav_id` (`rightav_id`),
+ KEY `wrongimage_id` (`wrongimage_id`),
+ KEY `wrongav_id` (`wrongav_id`),
+ CONSTRAINT `charactergroupsqueststations_ibfk_1` FOREIGN KEY (`charactergroupsquest_id`) REFERENCES `charactergroupsquests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsqueststations_ibfk_2` FOREIGN KEY (`stationpicture_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `charactergroupsqueststations_ibfk_3` FOREIGN KEY (`stationtype_id`) REFERENCES `stationtypes` (`id`),
+ CONSTRAINT `charactergroupsqueststations_ibfk_4` FOREIGN KEY (`rightimage_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `charactergroupsqueststations_ibfk_5` FOREIGN KEY (`rightav_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `charactergroupsqueststations_ibfk_6` FOREIGN KEY (`wrongimage_id`) REFERENCES `seminarymedia` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
+ CONSTRAINT `charactergroupsqueststations_ibfk_7` FOREIGN KEY (`wrongav_id`) REFERENCES `seminarymedia` (`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 `charactergroupsqueststations_charactergroups`
+--
+
+DROP TABLE IF EXISTS `charactergroupsqueststations_charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactergroupsqueststations_charactergroups` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `charactergroupsqueststation_id` int(11) NOT NULL,
+ `charactergroup_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `status` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `charactergroupsqueststation_id` (`charactergroupsqueststation_id`),
+ KEY `charactergroup_id` (`charactergroup_id`),
+ CONSTRAINT `charactergroupsqueststations_charactergroups_ibfk_1` FOREIGN KEY (`charactergroupsqueststation_id`) REFERENCES `charactergroupsqueststations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `charactergroupsqueststations_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 */;
+
+--
+-- 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,
+ `gender` bit(1) DEFAULT NULL,
+ `charactertitle_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`),
+ KEY `charactertype_id` (`charactertype_id`),
+ KEY `user_id` (`user_id`),
+ KEY `charactertitle_id` (`charactertitle_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`),
+ CONSTRAINT `characters_ibfk_3` FOREIGN KEY (`charactertitle_id`) REFERENCES `charactertitles` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
+) 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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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_charactertitles`
+--
+
+DROP TABLE IF EXISTS `characters_charactertitles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `characters_charactertitles` (
+ `character_id` int(11) NOT NULL,
+ `charactertitle_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`character_id`,`charactertitle_id`),
+ KEY `charactertitle_id` (`charactertitle_id`),
+ CONSTRAINT `characters_charactertitles_ibfk_1` FOREIGN KEY (`character_id`) REFERENCES `characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `characters_charactertitles_ibfk_2` FOREIGN KEY (`charactertitle_id`) REFERENCES `charactertitles` (`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 `charactertitles`
+--
+
+DROP TABLE IF EXISTS `charactertitles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `charactertitles` (
+ `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,
+ `hash` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `title_male` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `title_female` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `hash` (`hash`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `seminary_id` (`seminary_id`),
+ CONSTRAINT `charactertitles_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `charactertitles_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 `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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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,
+ `latitude` decimal(10,6) DEFAULT NULL,
+ `longitude` decimal(10,6) DEFAULT 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 `stationtypes`
+--
+
+DROP TABLE IF EXISTS `stationtypes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes` (
+ `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 `stationtypes_keyword`
+--
+
+DROP TABLE IF EXISTS `stationtypes_keyword`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes_keyword` (
+ `station_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `keyword_regex` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`station_id`),
+ KEY `created_user_id` (`created_user_id`),
+ CONSTRAINT `stationtypes_keyword_ibfk_1` FOREIGN KEY (`station_id`) REFERENCES `charactergroupsqueststations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `stationtypes_keyword_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 `stationtypes_keyword_charactergroups`
+--
+
+DROP TABLE IF EXISTS `stationtypes_keyword_charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes_keyword_charactergroups` (
+ `station_id` int(11) NOT NULL,
+ `charactergroup_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `keyword` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`station_id`,`charactergroup_id`),
+ KEY `charactergroup_id` (`charactergroup_id`),
+ CONSTRAINT `stationtypes_keyword_charactergroups_ibfk_1` FOREIGN KEY (`station_id`) REFERENCES `charactergroupsqueststations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `stationtypes_keyword_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 */;
+
+--
+-- Table structure for table `stationtypes_multiplechoice`
+--
+
+DROP TABLE IF EXISTS `stationtypes_multiplechoice`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes_multiplechoice` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `station_id` int(11) NOT NULL,
+ `pos` int(11) NOT NULL,
+ `answer` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `tick` tinyint(1) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `questtypes_multiplechoice_id_2` (`station_id`,`pos`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `questtypes_multiplechoice_id` (`station_id`),
+ CONSTRAINT `stationtypes_multiplechoice_ibfk_1` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `stationtypes_multiplechoice_ibfk_2` FOREIGN KEY (`station_id`) REFERENCES `charactergroupsqueststations` (`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 `stationtypes_multiplechoice_charactergroups`
+--
+
+DROP TABLE IF EXISTS `stationtypes_multiplechoice_charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes_multiplechoice_charactergroups` (
+ `stationtypes_multiplechoice_id` int(11) NOT NULL,
+ `charactergroup_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `ticked` tinyint(1) NOT NULL,
+ PRIMARY KEY (`stationtypes_multiplechoice_id`,`charactergroup_id`),
+ KEY `character_id` (`charactergroup_id`),
+ CONSTRAINT `stationtypes_multiplechoice_charactergroups_ibfk_1` FOREIGN KEY (`stationtypes_multiplechoice_id`) REFERENCES `stationtypes_multiplechoice` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `stationtypes_multiplechoice_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 */;
+
+--
+-- Table structure for table `stationtypes_singlechoice`
+--
+
+DROP TABLE IF EXISTS `stationtypes_singlechoice`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes_singlechoice` (
+ `station_id` int(11) NOT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_user_id` int(11) NOT NULL,
+ `answer_id` int(11) DEFAULT NULL,
+ PRIMARY KEY (`station_id`),
+ KEY `created_user_id` (`created_user_id`),
+ KEY `answer_id` (`answer_id`),
+ CONSTRAINT `stationtypes_singlechoice_ibfk_1` FOREIGN KEY (`station_id`) REFERENCES `charactergroupsqueststations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `stationtypes_singlechoice_ibfk_2` FOREIGN KEY (`created_user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `stationtypes_singlechoice_ibfk_3` FOREIGN KEY (`answer_id`) REFERENCES `stationtypes_singlechoice_answers` (`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 `stationtypes_singlechoice_answers`
+--
+
+DROP TABLE IF EXISTS `stationtypes_singlechoice_answers`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes_singlechoice_answers` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `stationtypes_singlechoice_station_id` int(11) NOT NULL,
+ `pos` int(11) NOT NULL,
+ `answer` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `station_id_2` (`stationtypes_singlechoice_station_id`,`pos`),
+ KEY `station_id` (`stationtypes_singlechoice_station_id`),
+ CONSTRAINT `stationtypes_singlechoice_answers_ibfk_1` FOREIGN KEY (`stationtypes_singlechoice_station_id`) REFERENCES `stationtypes_singlechoice` (`station_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 `stationtypes_singlechoice_charactergroups`
+--
+
+DROP TABLE IF EXISTS `stationtypes_singlechoice_charactergroups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `stationtypes_singlechoice_charactergroups` (
+ `stationtypes_singlechoice_station_id` int(11) NOT NULL,
+ `charactergroup_id` int(11) NOT NULL,
+ `answer_id` int(11) DEFAULT NULL,
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`stationtypes_singlechoice_station_id`,`charactergroup_id`),
+ KEY `answer_id` (`answer_id`),
+ KEY `charactergroup_id` (`charactergroup_id`),
+ CONSTRAINT `stationtypes_singlechoice_charactergroups_ibfk_1` FOREIGN KEY (`stationtypes_singlechoice_station_id`) REFERENCES `stationtypes_singlechoice` (`station_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `stationtypes_singlechoice_charactergroups_ibfk_2` FOREIGN KEY (`charactergroup_id`) REFERENCES `charactergroups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `stationtypes_singlechoice_charactergroups_ibfk_3` FOREIGN KEY (`answer_id`) REFERENCES `stationtypes_singlechoice_answers` (`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 `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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 2016-03-26 19:13:31
diff --git a/db/drop.sql b/db/drop.sql
new file mode 100644
index 00000000..089cc366
--- /dev/null
+++ b/db/drop.sql
@@ -0,0 +1,27 @@
+/** 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;
+DROP TRIGGER IF EXISTS update_cache_characters_xplevels_insert;
+DROP TRIGGER IF EXISTS update_cache_characters_xplevels_update;
+DROP TRIGGER IF EXISTS update_cache_characters_xplevels_delete;
+
+
+/** 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..38958e4c
--- /dev/null
+++ b/db/import.sql
@@ -0,0 +1,790 @@
+-- MySQL dump 10.16 Distrib 10.1.9-MariaDB, for Linux (x86_64)
+--
+-- Host: localhost Database: z
+-- ------------------------------------------------------
+-- Server version 10.1.9-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_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` (`id`, `created`, `condition`) 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'),(5,'2016-03-06 11:47:50','qrcode');
+/*!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 `charactergroupsqueststations`
+--
+
+LOCK TABLES `charactergroupsqueststations` WRITE;
+/*!40000 ALTER TABLE `charactergroupsqueststations` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroupsqueststations` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `charactergroupsqueststations_charactergroups`
+--
+
+LOCK TABLES `charactergroupsqueststations_charactergroups` WRITE;
+/*!40000 ALTER TABLE `charactergroupsqueststations_charactergroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `charactergroupsqueststations_charactergroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `characterroles`
+--
+
+LOCK TABLES `characterroles` WRITE;
+/*!40000 ALTER TABLE `characterroles` DISABLE KEYS */;
+INSERT INTO `characterroles` (`id`, `created`, `name`) 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 `pages`
+--
+
+LOCK TABLES `pages` WRITE;
+/*!40000 ALTER TABLE `pages` DISABLE KEYS */;
+/*!40000 ALTER TABLE `pages` 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` (`id`, `created`, `type`, `url`) 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` (`id`, `created`, `title`, `url`, `classname`) 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` (`id`, `created`, `title`, `url`) 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 `stationtypes`
+--
+
+LOCK TABLES `stationtypes` WRITE;
+/*!40000 ALTER TABLE `stationtypes` DISABLE KEYS */;
+INSERT INTO `stationtypes` (`id`, `created`, `title`, `url`, `classname`) VALUES (1,'2015-12-25 15:50:52','Empty','Empty',NULL),(2,'2015-12-25 15:50:52','Keyword','Keyword','keyword'),(3,'2015-12-25 15:51:16','Multiple Choice','Multiple-Choice','multiplechoice'),(4,'2016-02-27 17:22:11','Single Choice','Single-Choice','singlechoice');
+/*!40000 ALTER TABLE `stationtypes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `stationtypes_keyword`
+--
+
+LOCK TABLES `stationtypes_keyword` WRITE;
+/*!40000 ALTER TABLE `stationtypes_keyword` DISABLE KEYS */;
+/*!40000 ALTER TABLE `stationtypes_keyword` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `stationtypes_keyword_charactergroups`
+--
+
+LOCK TABLES `stationtypes_keyword_charactergroups` WRITE;
+/*!40000 ALTER TABLE `stationtypes_keyword_charactergroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `stationtypes_keyword_charactergroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `stationtypes_multiplechoice`
+--
+
+LOCK TABLES `stationtypes_multiplechoice` WRITE;
+/*!40000 ALTER TABLE `stationtypes_multiplechoice` DISABLE KEYS */;
+/*!40000 ALTER TABLE `stationtypes_multiplechoice` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `stationtypes_multiplechoice_charactergroups`
+--
+
+LOCK TABLES `stationtypes_multiplechoice_charactergroups` WRITE;
+/*!40000 ALTER TABLE `stationtypes_multiplechoice_charactergroups` DISABLE KEYS */;
+/*!40000 ALTER TABLE `stationtypes_multiplechoice_charactergroups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `userroles`
+--
+
+LOCK TABLES `userroles` WRITE;
+/*!40000 ALTER TABLE `userroles` DISABLE KEYS */;
+INSERT INTO `userroles` (`id`, `created`, `name`) 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` (`id`, `created`, `username`, `url`, `surname`, `prename`, `email`, `password`, `mailing`) 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` (`user_id`, `userrole_id`, `created`) 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-12-25 16:51:58
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+