From 2cf011e1caf9cb083b45e7d2840f827b529bf53e Mon Sep 17 00:00:00 2001
From: coderkun
Date: Sat, 22 Mar 2014 22:32:43 +0100
Subject: [PATCH] correct input fields for Questtype ?Textinput? when marker is
at the begin or end of text
---
.hgignore | 3 +
.htaccess | 50 ++
agents/BottomlevelAgent.inc | 25 +
agents/IntermediateAgent.inc | 48 ++
agents/ToplevelAgent.inc | 395 ++++++++++++
agents/bottomlevel/MenuAgent.inc | 35 +
.../QuestgroupshierarchypathAgent.inc | 35 +
.../bottomlevel/QuestgroupspictureAgent.inc | 35 +
agents/bottomlevel/UserrolesAgent.inc | 35 +
agents/intermediate/CharactergroupsAgent.inc | 35 +
.../CharactergroupsquestsAgent.inc | 35 +
agents/intermediate/CharactersAgent.inc | 43 ++
agents/intermediate/ErrorAgent.inc | 35 +
agents/intermediate/IntroductionAgent.inc | 35 +
agents/intermediate/MediaAgent.inc | 35 +
agents/intermediate/QuestgroupsAgent.inc | 37 ++
agents/intermediate/QuestsAgent.inc | 57 ++
agents/intermediate/SeminariesAgent.inc | 35 +
agents/intermediate/UsersAgent.inc | 44 ++
agents/toplevel/BinaryAgent.inc | 41 ++
agents/toplevel/FaultAgent.inc | 35 +
agents/toplevel/HtmlAgent.inc | 67 ++
apis/WebApi.inc | 250 ++++++++
app/Controller.inc | 99 +++
app/Model.inc | 42 ++
app/QuesttypeAgent.inc | 267 ++++++++
app/QuesttypeController.inc | 348 ++++++++++
app/QuesttypeModel.inc | 154 +++++
app/QuesttypeView.inc | 76 +++
app/ToplevelAgent.inc | 36 ++
app/Utils.inc | 83 +++
app/controllers/SeminaryRoleController.inc | 136 ++++
app/controllers/ToplevelController.inc | 170 +++++
.../QuesttypeAgentNotFoundException.inc | 77 +++
.../QuesttypeAgentNotValidException.inc | 77 +++
.../QuesttypeControllerNotFoundException.inc | 77 +++
.../QuesttypeControllerNotValidException.inc | 77 +++
.../QuesttypeModelNotFoundException.inc | 77 +++
.../QuesttypeModelNotValidException.inc | 77 +++
app/lib/Password.inc | 316 +++++++++
bootstrap.inc | 33 +
configs/AppConfig.inc | 126 ++++
configs/CoreConfig.inc | 167 +++++
controllers/BinaryController.inc | 37 ++
controllers/CharactergroupsController.inc | 143 +++++
.../CharactergroupsquestsController.inc | 91 +++
controllers/CharactersController.inc | 111 ++++
controllers/ErrorController.inc | 48 ++
controllers/FaultController.inc | 37 ++
controllers/HtmlController.inc | 59 ++
controllers/IntroductionController.inc | 35 +
controllers/MediaController.inc | 240 +++++++
controllers/MenuController.inc | 50 ++
controllers/QuestgroupsController.inc | 138 ++++
.../QuestgroupshierarchypathController.inc | 76 +++
controllers/QuestgroupspictureController.inc | 65 ++
controllers/QuestsController.inc | 558 ++++++++++++++++
controllers/SeminariesController.inc | 238 +++++++
controllers/UserrolesController.inc | 47 ++
controllers/UsersController.inc | 238 +++++++
.../components/AchievementComponent.inc | 41 ++
controllers/components/AuthComponent.inc | 79 +++
core/Agent.inc | 607 ++++++++++++++++++
core/Api.inc | 163 +++++
core/Autoloader.inc | 98 +++
core/ClassLoader.inc | 129 ++++
core/Component.inc | 85 +++
core/Config.inc | 49 ++
core/Controller.inc | 424 ++++++++++++
core/Driver.inc | 96 +++
core/Exception.inc | 65 ++
core/Linker.inc | 311 +++++++++
core/Logger.inc | 132 ++++
core/Model.inc | 141 ++++
core/Request.inc | 64 ++
core/Response.inc | 158 +++++
core/View.inc | 124 ++++
core/WebUtils.inc | 75 +++
drivers/DatabaseDriver.inc | 87 +++
drivers/MysqliDriver.inc | 169 +++++
exceptions/AccessDeniedException.inc | 51 ++
exceptions/ActionNotFoundException.inc | 77 +++
exceptions/AgentNotFoundException.inc | 67 ++
exceptions/AgentNotValidException.inc | 67 ++
exceptions/ClassNotFoundException.inc | 77 +++
exceptions/ClassNotValidException.inc | 77 +++
exceptions/ComponentNotFoundException.inc | 67 ++
exceptions/ComponentNotValidException.inc | 67 ++
exceptions/ControllerNotFoundException.inc | 67 ++
exceptions/ControllerNotValidException.inc | 67 ++
exceptions/DatamodelException.inc | 99 +++
exceptions/DriverNotFoundException.inc | 68 ++
exceptions/DriverNotValidException.inc | 68 ++
exceptions/FatalDatamodelException.inc | 96 +++
exceptions/IdNotFoundException.inc | 77 +++
exceptions/LayoutNotFoundException.inc | 68 ++
exceptions/LayoutNotValidException.inc | 67 ++
exceptions/ModelNotFoundException.inc | 67 ++
exceptions/ModelNotValidException.inc | 68 ++
exceptions/ParamsNotValidException.inc | 77 +++
exceptions/ServiceUnavailableException.inc | 77 +++
exceptions/ViewNotFoundException.inc | 77 +++
locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 2782 bytes
locale/de_DE/LC_MESSAGES/The Legend of Z.po | 270 ++++++++
logs/empty | 0
media/empty | 0
models/AchievementsModel.inc | 36 ++
models/CharactergroupsModel.inc | 147 +++++
models/CharactergroupsquestsModel.inc | 136 ++++
models/CharactersModel.inc | 209 ++++++
models/DatabaseModel.inc | 83 +++
models/MediaModel.inc | 90 +++
models/QuestgroupsModel.inc | 424 ++++++++++++
models/QuestgroupshierarchyModel.inc | 103 +++
models/QuestsModel.inc | 344 ++++++++++
models/QuesttextsModel.inc | 135 ++++
models/QuesttypesModel.inc | 62 ++
models/SeminariesModel.inc | 161 +++++
models/SeminarycharacterfieldsModel.inc | 58 ++
models/UserrolesModel.inc | 77 +++
models/UsersModel.inc | 249 +++++++
models/UserseminaryrolesModel.inc | 78 +++
.../dragndrop/DragndropQuesttypeAgent.inc | 24 +
.../DragndropQuesttypeController.inc | 148 +++++
.../dragndrop/DragndropQuesttypeModel.inc | 109 ++++
questtypes/dragndrop/html/quest.tpl | 17 +
questtypes/dragndrop/html/submission.tpl | 7 +
questtypes/dummy/DummyQuesttypeAgent.inc | 24 +
questtypes/dummy/DummyQuesttypeController.inc | 92 +++
questtypes/dummy/DummyQuesttypeModel.inc | 25 +
questtypes/dummy/html/quest.tpl | 4 +
questtypes/dummy/html/submission.tpl | 0
.../MultiplechoiceQuesttypeAgent.inc | 24 +
.../MultiplechoiceQuesttypeController.inc | 143 +++++
.../MultiplechoiceQuesttypeModel.inc | 111 ++++
questtypes/multiplechoice/html/quest.tpl | 11 +
questtypes/multiplechoice/html/submission.tpl | 9 +
questtypes/submit/SubmitQuesttypeAgent.inc | 24 +
.../submit/SubmitQuesttypeController.inc | 129 ++++
questtypes/submit/SubmitQuesttypeModel.inc | 72 +++
questtypes/submit/html/quest.tpl | 14 +
questtypes/submit/html/submission.tpl | 11 +
.../textinput/TextinputQuesttypeAgent.inc | 24 +
.../TextinputQuesttypeController.inc | 170 +++++
.../textinput/TextinputQuesttypeModel.inc | 117 ++++
questtypes/textinput/html/quest.tpl | 11 +
questtypes/textinput/html/submission.tpl | 7 +
requests/WebRequest.inc | 401 ++++++++++++
responses/WebResponse.inc | 250 ++++++++
tmp/empty | 0
views/binary/binary.tpl | 1 +
views/binary/error/index.tpl | 1 +
views/binary/media/index.tpl | 1 +
views/error.tpl | 13 +
views/fault/error/index.tpl | 2 +
views/fault/fault.tpl | 16 +
views/html/charactergroups/group.tpl | 33 +
views/html/charactergroups/groupsgroup.tpl | 18 +
views/html/charactergroups/index.tpl | 9 +
views/html/charactergroupsquests/quest.tpl | 46 ++
views/html/characters/character.tpl | 25 +
views/html/characters/index.tpl | 8 +
views/html/error/index.tpl | 2 +
views/html/html.tpl | 40 ++
views/html/introduction/index.tpl | 21 +
views/html/menu/index.tpl | 14 +
views/html/questgroups/questgroup.tpl | 56 ++
views/html/questgroupshierarchypath/index.tpl | 7 +
views/html/questgroupspicture/index.tpl | 3 +
views/html/quests/index.tpl | 0
views/html/quests/quest.tpl | 85 +++
views/html/quests/submission.tpl | 13 +
views/html/quests/submissions.tpl | 35 +
views/html/seminaries/create.tpl | 10 +
views/html/seminaries/delete.tpl | 8 +
views/html/seminaries/edit.tpl | 10 +
views/html/seminaries/index.tpl | 14 +
views/html/seminaries/seminary.tpl | 46 ++
views/html/userroles/user.tpl | 5 +
views/html/users/create.tpl | 14 +
views/html/users/delete.tpl | 8 +
views/html/users/edit.tpl | 14 +
views/html/users/index.tpl | 14 +
views/html/users/login.tpl | 12 +
views/html/users/logout.tpl | 0
views/html/users/user.tpl | 19 +
views/inlineerror.tpl | 1 +
www/.htaccess | 8 +
www/css/desktop.css | 124 ++++
www/error403.html | 14 +
www/error404.html | 14 +
www/error500.html | 14 +
www/index.php | 45 ++
193 files changed, 16344 insertions(+)
create mode 100644 .hgignore
create mode 100644 .htaccess
create mode 100644 agents/BottomlevelAgent.inc
create mode 100644 agents/IntermediateAgent.inc
create mode 100644 agents/ToplevelAgent.inc
create mode 100644 agents/bottomlevel/MenuAgent.inc
create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc
create mode 100644 agents/bottomlevel/QuestgroupspictureAgent.inc
create mode 100644 agents/bottomlevel/UserrolesAgent.inc
create mode 100644 agents/intermediate/CharactergroupsAgent.inc
create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc
create mode 100644 agents/intermediate/CharactersAgent.inc
create mode 100644 agents/intermediate/ErrorAgent.inc
create mode 100644 agents/intermediate/IntroductionAgent.inc
create mode 100644 agents/intermediate/MediaAgent.inc
create mode 100644 agents/intermediate/QuestgroupsAgent.inc
create mode 100644 agents/intermediate/QuestsAgent.inc
create mode 100644 agents/intermediate/SeminariesAgent.inc
create mode 100644 agents/intermediate/UsersAgent.inc
create mode 100644 agents/toplevel/BinaryAgent.inc
create mode 100644 agents/toplevel/FaultAgent.inc
create mode 100644 agents/toplevel/HtmlAgent.inc
create mode 100644 apis/WebApi.inc
create mode 100644 app/Controller.inc
create mode 100644 app/Model.inc
create mode 100644 app/QuesttypeAgent.inc
create mode 100644 app/QuesttypeController.inc
create mode 100644 app/QuesttypeModel.inc
create mode 100644 app/QuesttypeView.inc
create mode 100644 app/ToplevelAgent.inc
create mode 100644 app/Utils.inc
create mode 100644 app/controllers/SeminaryRoleController.inc
create mode 100644 app/controllers/ToplevelController.inc
create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc
create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc
create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc
create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc
create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc
create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc
create mode 100644 app/lib/Password.inc
create mode 100644 bootstrap.inc
create mode 100644 configs/AppConfig.inc
create mode 100644 configs/CoreConfig.inc
create mode 100644 controllers/BinaryController.inc
create mode 100644 controllers/CharactergroupsController.inc
create mode 100644 controllers/CharactergroupsquestsController.inc
create mode 100644 controllers/CharactersController.inc
create mode 100644 controllers/ErrorController.inc
create mode 100644 controllers/FaultController.inc
create mode 100644 controllers/HtmlController.inc
create mode 100644 controllers/IntroductionController.inc
create mode 100644 controllers/MediaController.inc
create mode 100644 controllers/MenuController.inc
create mode 100644 controllers/QuestgroupsController.inc
create mode 100644 controllers/QuestgroupshierarchypathController.inc
create mode 100644 controllers/QuestgroupspictureController.inc
create mode 100644 controllers/QuestsController.inc
create mode 100644 controllers/SeminariesController.inc
create mode 100644 controllers/UserrolesController.inc
create mode 100644 controllers/UsersController.inc
create mode 100644 controllers/components/AchievementComponent.inc
create mode 100644 controllers/components/AuthComponent.inc
create mode 100644 core/Agent.inc
create mode 100644 core/Api.inc
create mode 100644 core/Autoloader.inc
create mode 100644 core/ClassLoader.inc
create mode 100644 core/Component.inc
create mode 100644 core/Config.inc
create mode 100644 core/Controller.inc
create mode 100644 core/Driver.inc
create mode 100644 core/Exception.inc
create mode 100644 core/Linker.inc
create mode 100644 core/Logger.inc
create mode 100644 core/Model.inc
create mode 100644 core/Request.inc
create mode 100644 core/Response.inc
create mode 100644 core/View.inc
create mode 100644 core/WebUtils.inc
create mode 100644 drivers/DatabaseDriver.inc
create mode 100644 drivers/MysqliDriver.inc
create mode 100644 exceptions/AccessDeniedException.inc
create mode 100644 exceptions/ActionNotFoundException.inc
create mode 100644 exceptions/AgentNotFoundException.inc
create mode 100644 exceptions/AgentNotValidException.inc
create mode 100644 exceptions/ClassNotFoundException.inc
create mode 100644 exceptions/ClassNotValidException.inc
create mode 100644 exceptions/ComponentNotFoundException.inc
create mode 100644 exceptions/ComponentNotValidException.inc
create mode 100644 exceptions/ControllerNotFoundException.inc
create mode 100644 exceptions/ControllerNotValidException.inc
create mode 100644 exceptions/DatamodelException.inc
create mode 100644 exceptions/DriverNotFoundException.inc
create mode 100644 exceptions/DriverNotValidException.inc
create mode 100644 exceptions/FatalDatamodelException.inc
create mode 100644 exceptions/IdNotFoundException.inc
create mode 100644 exceptions/LayoutNotFoundException.inc
create mode 100644 exceptions/LayoutNotValidException.inc
create mode 100644 exceptions/ModelNotFoundException.inc
create mode 100644 exceptions/ModelNotValidException.inc
create mode 100644 exceptions/ParamsNotValidException.inc
create mode 100644 exceptions/ServiceUnavailableException.inc
create mode 100644 exceptions/ViewNotFoundException.inc
create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo
create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po
create mode 100644 logs/empty
create mode 100644 media/empty
create mode 100644 models/AchievementsModel.inc
create mode 100644 models/CharactergroupsModel.inc
create mode 100644 models/CharactergroupsquestsModel.inc
create mode 100644 models/CharactersModel.inc
create mode 100644 models/DatabaseModel.inc
create mode 100644 models/MediaModel.inc
create mode 100644 models/QuestgroupsModel.inc
create mode 100644 models/QuestgroupshierarchyModel.inc
create mode 100644 models/QuestsModel.inc
create mode 100644 models/QuesttextsModel.inc
create mode 100644 models/QuesttypesModel.inc
create mode 100644 models/SeminariesModel.inc
create mode 100644 models/SeminarycharacterfieldsModel.inc
create mode 100644 models/UserrolesModel.inc
create mode 100644 models/UsersModel.inc
create mode 100644 models/UserseminaryrolesModel.inc
create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc
create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc
create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc
create mode 100644 questtypes/dragndrop/html/quest.tpl
create mode 100644 questtypes/dragndrop/html/submission.tpl
create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc
create mode 100644 questtypes/dummy/DummyQuesttypeController.inc
create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc
create mode 100644 questtypes/dummy/html/quest.tpl
create mode 100644 questtypes/dummy/html/submission.tpl
create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc
create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc
create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc
create mode 100644 questtypes/multiplechoice/html/quest.tpl
create mode 100644 questtypes/multiplechoice/html/submission.tpl
create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc
create mode 100644 questtypes/submit/SubmitQuesttypeController.inc
create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc
create mode 100644 questtypes/submit/html/quest.tpl
create mode 100644 questtypes/submit/html/submission.tpl
create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc
create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc
create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc
create mode 100644 questtypes/textinput/html/quest.tpl
create mode 100644 questtypes/textinput/html/submission.tpl
create mode 100644 requests/WebRequest.inc
create mode 100644 responses/WebResponse.inc
create mode 100644 tmp/empty
create mode 100644 views/binary/binary.tpl
create mode 100644 views/binary/error/index.tpl
create mode 100644 views/binary/media/index.tpl
create mode 100644 views/error.tpl
create mode 100644 views/fault/error/index.tpl
create mode 100644 views/fault/fault.tpl
create mode 100644 views/html/charactergroups/group.tpl
create mode 100644 views/html/charactergroups/groupsgroup.tpl
create mode 100644 views/html/charactergroups/index.tpl
create mode 100644 views/html/charactergroupsquests/quest.tpl
create mode 100644 views/html/characters/character.tpl
create mode 100644 views/html/characters/index.tpl
create mode 100644 views/html/error/index.tpl
create mode 100644 views/html/html.tpl
create mode 100644 views/html/introduction/index.tpl
create mode 100644 views/html/menu/index.tpl
create mode 100644 views/html/questgroups/questgroup.tpl
create mode 100644 views/html/questgroupshierarchypath/index.tpl
create mode 100644 views/html/questgroupspicture/index.tpl
create mode 100644 views/html/quests/index.tpl
create mode 100644 views/html/quests/quest.tpl
create mode 100644 views/html/quests/submission.tpl
create mode 100644 views/html/quests/submissions.tpl
create mode 100644 views/html/seminaries/create.tpl
create mode 100644 views/html/seminaries/delete.tpl
create mode 100644 views/html/seminaries/edit.tpl
create mode 100644 views/html/seminaries/index.tpl
create mode 100644 views/html/seminaries/seminary.tpl
create mode 100644 views/html/userroles/user.tpl
create mode 100644 views/html/users/create.tpl
create mode 100644 views/html/users/delete.tpl
create mode 100644 views/html/users/edit.tpl
create mode 100644 views/html/users/index.tpl
create mode 100644 views/html/users/login.tpl
create mode 100644 views/html/users/logout.tpl
create mode 100644 views/html/users/user.tpl
create mode 100644 views/inlineerror.tpl
create mode 100644 www/.htaccess
create mode 100644 www/css/desktop.css
create mode 100644 www/error403.html
create mode 100644 www/error404.html
create mode 100644 www/error500.html
create mode 100644 www/index.php
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 00000000..822d53ff
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,3 @@
+syntax: glob
+media/*
+tmp/*
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 00000000..cb3fb9ef
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,50 @@
+Options -Indexes -MultiViews
+
+ErrorDocument 403 /www/error403.html
+ErrorDocument 404 /www/error404.html
+ErrorDocument 500 /www/error500.html
+
+
+
+
+ Require all granted
+
+
+ Require all denied
+
+
+
+ Require all denied
+
+
+
+ Require all denied
+
+
+
+
+ Allow From All
+
+
+ Order Deny,Allow
+ Deny From All
+
+
+
+ Order Deny,Allow
+ Deny From All
+
+
+
+ Order Deny,Allow
+ Deny From All
+
+
+
+
+
+ RewriteEngine On
+
+ RewriteBase /
+ RewriteRule ^(.*)$ www/$1 [L]
+
diff --git a/agents/BottomlevelAgent.inc b/agents/BottomlevelAgent.inc
new file mode 100644
index 00000000..37816614
--- /dev/null
+++ b/agents/BottomlevelAgent.inc
@@ -0,0 +1,25 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\agents;
+
+
+ /**
+ * The BottomlevelAgent is the standard Agent and can have indefinite
+ * SubAgents.
+ *
+ * @author coderkun
+ */
+ abstract class BottomlevelAgent extends \nre\core\Agent
+ {
+ }
+
+?>
diff --git a/agents/IntermediateAgent.inc b/agents/IntermediateAgent.inc
new file mode 100644
index 00000000..7139653d
--- /dev/null
+++ b/agents/IntermediateAgent.inc
@@ -0,0 +1,48 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\agents;
+
+
+ /**
+ * The IntermediateAgent assumes the task of a module. There is only one
+ * IntermediateAgent per request.
+ *
+ * @author coderkun
+ */
+ abstract class IntermediateAgent extends \nre\core\Agent
+ {
+
+
+
+
+ /**
+ * Get the layout if it was explicitly defined.
+ *
+ * @return string Layout of the IntermediateAgent
+ */
+ public static function getLayout($agentName)
+ {
+ // Determine classname
+ $className = Autoloader::concatClassNames($agentName, 'Agent');
+
+ // Check property
+ if(isset($className::$layout)) {
+ return $className::$layout;
+ }
+
+
+ return null;
+ }
+
+ }
+
+?>
diff --git a/agents/ToplevelAgent.inc b/agents/ToplevelAgent.inc
new file mode 100644
index 00000000..9d6d6f8a
--- /dev/null
+++ b/agents/ToplevelAgent.inc
@@ -0,0 +1,395 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\agents;
+
+
+ /**
+ * The ToplevelAgent assumes the task of a FrontController. There is
+ * only one per request.
+ *
+ * @author coderkun
+ */
+ class ToplevelAgent extends \nre\core\Agent
+ {
+ /**
+ * Stage: Load
+ *
+ * @var string
+ */
+ const STAGE_LOAD = 'load';
+ /**
+ * Stage: Run
+ *
+ * @var string
+ */
+ const STAGE_RUN = 'run';
+
+ /**
+ * Current request
+ *
+ * @var Request
+ */
+ private $request;
+ /**
+ * Current response
+ *
+ * @var Response
+ */
+ private $response;
+ /**
+ * Layout instace
+ *
+ * @var Layout
+ */
+ private $layout = null;
+ /**
+ * IntermediateAgent instance
+ *
+ * @var IntermediateAgent
+ */
+ private $intermediateAgent = null;
+
+
+
+
+ /**
+ * Construct a ToplevelAgent.
+ *
+ * @throws ServiceUnavailableException
+ * @throws DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ControllerNotValidException
+ * @throws ControllerNotFoundException
+ * @param Request $request Current request
+ * @param Response $respone Current response
+ * @param Logger $log Log-system
+ */
+ protected function __construct(\nre\core\Request $request, \nre\core\Response $response, \nre\core\Logger $log=null)
+ {
+ // Store values
+ $this->request = $request;
+ $this->response = $response;
+
+
+ // Create response
+ $response = clone $response;
+ $response->clearParams(1);
+ $response->addParams(
+ null,
+ \nre\configs\CoreConfig::$defaults['action']
+ );
+
+ // Call parent constructor
+ parent::__construct($request, $response, $log, true);
+
+
+ // Load IntermediateAgent
+ $this->loadIntermediateAgent();
+ }
+
+
+
+
+ /**
+ * Run the Controller of this Agent and its SubAgents.
+ *
+ * @throws ServiceUnavailableException
+ * @param Request $request Current request
+ * @param Response $response Current response
+ * @return Exception Last occurred exception of SubAgents
+ */
+ public function run(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ try {
+ return $this->_run($request, $response);
+ }
+ catch(\nre\exceptions\AccessDeniedException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_FORBIDDEN, self::STAGE_RUN);
+ }
+ catch(\nre\exceptions\ParamsNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND, self::STAGE_RUN);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND, self::STAGE_RUN);
+ }
+ catch(\nre\exceptions\DatamodelException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE, self::STAGE_RUN);
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND, self::STAGE_RUN);
+ }
+ }
+
+
+ /**
+ * 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())
+ {
+ // Render IntermediateAgent
+ $data = array();
+ $data['intermediate'] = $this->intermediateAgent->render();
+
+
+ // Render ToplevelAgent
+ return parent::render($data);
+ }
+
+
+ /**
+ * Return the IntermediateAgent.
+ *
+ * @return IntermediateAgent IntermediateAgent
+ */
+ public function getIntermediateAgent()
+ {
+ return $this->intermediateAgent;
+ }
+
+
+
+
+ /**
+ * Load a SubAgent and add it.
+ *
+ * @throws ServiceUnavailableException
+ * @throws FatalDatamodelException
+ * @throws AgentNotFoundException
+ * @throws 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(\nre\exceptions\DatamodelException $e) {
+ throw new \nre\exceptions\FatalDatamodelException($e->getDatamodelMessage(), $e->getDatamodelErrorNumber());
+ }
+ }
+
+
+
+
+ /**
+ * Load IntermediateAgent defined by the current request.
+ *
+ * @throws ServiceUnavailableException
+ */
+ private function loadIntermediateAgent()
+ {
+ try {
+ $this->_loadIntermediateAgent();
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
+ }
+ catch(\nre\exceptions\DatamodelException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\DriverNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\DriverNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ModelNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ModelNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ControllerNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ControllerNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
+ }
+ catch(\nre\exceptions\AgentNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\AgentNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
+ }
+ }
+
+
+ /**
+ * Load IntermediateAgent defined by the current request.
+ *
+ * @throws ServiceUnavailableException
+ */
+ private function _loadIntermediateAgent()
+ {
+ // Determine IntermediateAgent
+ $agentName = $this->response->getParam(1);
+ if(is_null($agentName)) {
+ $agentName = $this->request->getParam(1, 'intermediate');
+ $this->response->addParam($agentName);
+ }
+
+ // Load IntermediateAgent
+ IntermediateAgent::load($agentName);
+
+
+ // Determine Action
+ $action = $this->response->getParam(2);
+ if(is_null($action)) {
+ $action = $this->request->getParam(2, 'action');
+ $this->response->addParam($action);
+ }
+
+ // Construct IntermediateAgent
+ $this->intermediateAgent = \nre\agents\IntermediateAgent::factory(
+ $agentName,
+ $this->request,
+ $this->response,
+ $this->log
+ );
+ }
+
+
+ /**
+ * Run the Controller of this Agent and its SubAgents.
+ *
+ * @throws AccessDeniedException
+ * @throws IdNotFoundException
+ * @throws ServiceUnavailableException
+ * @throws DatamodelException
+ * @param Request $request Current request
+ * @param Response $response Current response
+ * @return Exception Last occurred exception of SubAgents
+ */
+ private function _run(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ // Run IntermediateAgent
+ $this->runIntermediateAgent();
+
+
+ // TODO Request instead of response?
+ $response = clone $response;
+ $response->clearParams(2);
+ $response->addParam(\nre\configs\CoreConfig::$defaults['action']);
+
+
+ // Run ToplevelAgent
+ return parent::run($request, $response);
+ }
+
+
+ /**
+ * Run IntermediateAgent.
+ *
+ * @throws AccessDeniedException
+ * @throws ParamsNotValidException
+ * @throws IdNotFoundException
+ * @throws ServiceUnavailableException
+ * @throws DatamodelException
+ */
+ private function runIntermediateAgent()
+ {
+ $this->intermediateAgent->run(
+ $this->request,
+ $this->response
+ );
+ }
+
+
+ /**
+ * Handle an error that occurred during
+ * loading/cnostructing/running of the IntermediateAgent.
+ *
+ * @throws ServiceUnavailableException
+ * @param Exception $exception Occurred exception
+ * @param int $httpStatusCode HTTP-statuscode
+ * @param string $stage Stage of execution
+ */
+ private function error($exception, $httpStatusCode, $stage=self::STAGE_LOAD)
+ {
+ // Log error
+ $this->log($exception, \nre\core\Logger::LOGMODE_AUTO);
+
+
+ try {
+ // Define ErrorAgent
+ $this->response->clearParams(1);
+ $this->response->addParams(
+ \nre\configs\AppConfig::$defaults['intermediate-error'],
+ \nre\configs\CoreConfig::$defaults['action'],
+ $httpStatusCode
+ );
+
+ // Load ErrorAgent
+ $this->_loadIntermediateAgent();
+
+ // Run ErrorAgent
+ if($stage == self::STAGE_RUN) {
+ $this->_run($this->request, $this->response);
+ }
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\DatamodelException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\DriverNotValidException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\DriverNotFoundException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\ModelNotValidException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\ModelNotFoundException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\ControllerNotValidException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\ControllerNotFoundException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\AgentNotValidException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(\nre\exceptions\AgentNotFoundException $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ catch(Exception $e) {
+ throw new \nre\exceptions\ServiceUnavailableException($e);
+ }
+ }
+
+
+ }
+
+?>
diff --git a/agents/bottomlevel/MenuAgent.inc b/agents/bottomlevel/MenuAgent.inc
new file mode 100644
index 00000000..77d60d8d
--- /dev/null
+++ b/agents/bottomlevel/MenuAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\bottomlevel;
+
+
+ /**
+ * Agent to display a menu.
+ *
+ * @author Oliver Hanraths
+ */
+ class MenuAgent extends \nre\agents\BottomlevelAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/bottomlevel/QuestgroupshierarchypathAgent.inc b/agents/bottomlevel/QuestgroupshierarchypathAgent.inc
new file mode 100644
index 00000000..a2cb8086
--- /dev/null
+++ b/agents/bottomlevel/QuestgroupshierarchypathAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\bottomlevel;
+
+
+ /**
+ * Agent to display the Questgroups hierarchy path.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupshierarchypathAgent extends \nre\agents\BottomlevelAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/bottomlevel/QuestgroupspictureAgent.inc b/agents/bottomlevel/QuestgroupspictureAgent.inc
new file mode 100644
index 00000000..8cdca7cd
--- /dev/null
+++ b/agents/bottomlevel/QuestgroupspictureAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\bottomlevel;
+
+
+ /**
+ * Agent to display the picture of a Questgroup.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupspictureAgent extends \nre\agents\BottomlevelAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/bottomlevel/UserrolesAgent.inc b/agents/bottomlevel/UserrolesAgent.inc
new file mode 100644
index 00000000..b6d6e65b
--- /dev/null
+++ b/agents/bottomlevel/UserrolesAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\bottomlevel;
+
+
+ /**
+ * Agent to display and manage userroles.
+ *
+ * @author Oliver Hanraths
+ */
+ class UserrolesAgent extends \nre\agents\BottomlevelAgent
+ {
+
+
+
+
+ /**
+ * Action: user.
+ */
+ public function user(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/CharactergroupsAgent.inc b/agents/intermediate/CharactergroupsAgent.inc
new file mode 100644
index 00000000..77ac5f5a
--- /dev/null
+++ b/agents/intermediate/CharactergroupsAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to display Character groups.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/CharactergroupsquestsAgent.inc b/agents/intermediate/CharactergroupsquestsAgent.inc
new file mode 100644
index 00000000..bfc87de1
--- /dev/null
+++ b/agents/intermediate/CharactergroupsquestsAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to display Character groups Quests.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsquestsAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/CharactersAgent.inc b/agents/intermediate/CharactersAgent.inc
new file mode 100644
index 00000000..25102198
--- /dev/null
+++ b/agents/intermediate/CharactersAgent.inc
@@ -0,0 +1,43 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to list registered Characters and their data.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactersAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+
+ /**
+ * Action: character.
+ */
+ public function character(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/ErrorAgent.inc b/agents/intermediate/ErrorAgent.inc
new file mode 100644
index 00000000..85bb6f95
--- /dev/null
+++ b/agents/intermediate/ErrorAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to show an error page.
+ *
+ * @author Oliver Hanraths
+ */
+ class ErrorAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/IntroductionAgent.inc b/agents/intermediate/IntroductionAgent.inc
new file mode 100644
index 00000000..3a06fdff
--- /dev/null
+++ b/agents/intermediate/IntroductionAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to show an introduction page.
+ *
+ * @author Oliver Hanraths
+ */
+ class IntroductionAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/MediaAgent.inc b/agents/intermediate/MediaAgent.inc
new file mode 100644
index 00000000..c3fd35ae
--- /dev/null
+++ b/agents/intermediate/MediaAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to process and show media.
+ *
+ * @author Oliver Hanraths
+ */
+ class MediaAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/QuestgroupsAgent.inc b/agents/intermediate/QuestgroupsAgent.inc
new file mode 100644
index 00000000..860fd177
--- /dev/null
+++ b/agents/intermediate/QuestgroupsAgent.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\agents\intermediate;
+
+
+ /**
+ * Agent to display Questgroups.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupsAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: questgroup.
+ */
+ public function questgroup(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ $this->addSubAgent('Questgroupshierarchypath', 'index', $request->getParam(3), $request->getParam(4));
+ $this->addSubAgent('Questgroupspicture', 'index', $request->getParam(3), $request->getParam(4), true);
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc
new file mode 100644
index 00000000..1a823fa6
--- /dev/null
+++ b/agents/intermediate/QuestsAgent.inc
@@ -0,0 +1,57 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to display Quests.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestsAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: quest.
+ */
+ public function quest(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ $this->addSubAgent('Questgroupshierarchypath', 'index', $request->getParam(3), $request->getParam(4), true);
+ $this->addSubAgent('Questgroupspicture', 'index', $request->getParam(3), $request->getParam(4), true);
+ }
+
+
+ /**
+ * Action: submissions.
+ */
+ public function submissions(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ $this->addSubAgent('Questgroupshierarchypath', 'index', $request->getParam(3), $request->getParam(4), true);
+ $this->addSubAgent('Questgroupspicture', 'index', $request->getParam(3), $request->getParam(4), true);
+ }
+
+
+ /**
+ * Action: submission.
+ */
+ public function submission(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ $this->addSubAgent('Questgroupshierarchypath', 'index', $request->getParam(3), $request->getParam(4), true);
+ $this->addSubAgent('Questgroupspicture', 'index', $request->getParam(3), $request->getParam(4), true);
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/SeminariesAgent.inc b/agents/intermediate/SeminariesAgent.inc
new file mode 100644
index 00000000..53f08124
--- /dev/null
+++ b/agents/intermediate/SeminariesAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to list registered seminaries.
+ *
+ * @author Oliver Hanraths
+ */
+ class SeminariesAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/intermediate/UsersAgent.inc b/agents/intermediate/UsersAgent.inc
new file mode 100644
index 00000000..a9094490
--- /dev/null
+++ b/agents/intermediate/UsersAgent.inc
@@ -0,0 +1,44 @@
+
+ * @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\agents\intermediate;
+
+
+ /**
+ * Agent to list registered users and their data.
+ *
+ * @author Oliver Hanraths
+ */
+ class UsersAgent extends \nre\agents\IntermediateAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+
+ /**
+ * Action: user.
+ */
+ public function user(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ $this->addSubAgent('Userroles', 'user');
+ }
+
+ }
+
+?>
diff --git a/agents/toplevel/BinaryAgent.inc b/agents/toplevel/BinaryAgent.inc
new file mode 100644
index 00000000..f2d6d33e
--- /dev/null
+++ b/agents/toplevel/BinaryAgent.inc
@@ -0,0 +1,41 @@
+
+ * @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\agents\toplevel;
+
+
+ /**
+ * Agent to display binary data (e. g. images).
+ *
+ * @author Oliver Hanraths
+ */
+ class BinaryAgent extends \hhu\z\ToplevelAgent
+ {
+
+
+
+
+ protected function __construct(\nre\core\Request $request, \nre\core\Response $response, \nre\core\Logger $log=null)
+ {
+ parent::__construct($request, $response, $log);
+ }
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/toplevel/FaultAgent.inc b/agents/toplevel/FaultAgent.inc
new file mode 100644
index 00000000..1ceb682e
--- /dev/null
+++ b/agents/toplevel/FaultAgent.inc
@@ -0,0 +1,35 @@
+
+ * @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\agents\toplevel;
+
+
+ /**
+ * Agent to display a toplevel error page.
+ *
+ * @author Oliver Hanraths
+ */
+ class FaultAgent extends \nre\agents\ToplevelAgent
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ }
+
+ }
+
+?>
diff --git a/agents/toplevel/HtmlAgent.inc b/agents/toplevel/HtmlAgent.inc
new file mode 100644
index 00000000..63ed4525
--- /dev/null
+++ b/agents/toplevel/HtmlAgent.inc
@@ -0,0 +1,67 @@
+
+ * @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\agents\toplevel;
+
+
+ /**
+ * Agent to display a HTML-page.
+ *
+ * @author Oliver Hanraths
+ */
+ class HtmlAgent extends \hhu\z\ToplevelAgent
+ {
+
+
+
+
+ protected function __construct(\nre\core\Request $request, \nre\core\Response $response, \nre\core\Logger $log=null)
+ {
+ parent::__construct($request, $response, $log);
+
+
+ $this->setLanguage($request);
+ }
+
+
+ /**
+ * Action: index.
+ */
+ public function index(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ // Add menu
+ $this->addSubAgent('Menu');
+ }
+
+
+
+
+ private function setLanguage(\nre\core\Request $request)
+ {
+ // Set domain
+ $domain = \nre\configs\AppConfig::$app['name'];
+
+ // Get language
+ $locale = $request->getGetParam('lang', 'language');
+ if(is_null($locale)) {
+ return;
+ }
+
+ // Load translation
+ putenv("LC_ALL=$locale");
+ setlocale(LC_ALL, $locale);
+ bindtextdomain($domain, ROOT.DS.\nre\configs\AppConfig::$dirs['locale']);
+ textdomain($domain);
+ }
+
+ }
+
+?>
diff --git a/apis/WebApi.inc b/apis/WebApi.inc
new file mode 100644
index 00000000..6851ee2d
--- /dev/null
+++ b/apis/WebApi.inc
@@ -0,0 +1,250 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\apis;
+
+
+ /**
+ * WebApi-implementation.
+ *
+ * This class runs and renders an web-applictaion.
+ *
+ * @author coderkun
+ */
+ class WebApi extends \nre\core\Api
+ {
+
+
+
+
+ /**
+ * Construct a new WebApi.
+ */
+ public function __construct()
+ {
+ parent::__construct(
+ new \nre\requests\WebRequest(),
+ new \nre\responses\WebResponse()
+ );
+
+ // Add routes
+ $this->addRoutes();
+
+ // Disable screen logging for AJAX requests
+ if($this->request->getParam(0, 'toplevel') == 'ajax') {
+ $this->log->disableAutoLogToScreen();
+ }
+ }
+
+
+
+
+ /**
+ * Run application.
+ *
+ * This method runs the application and handles all errors.
+ */
+ public function run()
+ {
+ try {
+ $exception = parent::run();
+
+ if(!is_null($exception)) {
+ $this->errorService($exception);
+ }
+ }
+ catch(\nre\exceptions\ServiceUnavailableException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
+ }
+ catch(\nre\exceptions\FatalDatamodelException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\DatamodelException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\DriverNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\DriverNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ModelNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ModelNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
+ }
+ catch(\nre\exceptions\ControllerNotValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\ControllerNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
+ }
+ catch(\nre\exceptions\AgentNoaatValidException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
+ }
+ catch(\nre\exceptions\AgentNotFoundException $e) {
+ $this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ $this->errorService($e);
+ }
+ }
+
+
+ /**
+ * Render output.
+ */
+ public function render()
+ {
+ // Generate output
+ parent::render();
+
+
+ // Set HTTP-header
+ $this->response->header();
+
+ // Show output
+ echo $this->response->getOutput();
+ }
+
+
+
+
+ /**
+ * Add routes (normal and reverse) defined in the AppConfig.
+ */
+ private function addRoutes()
+ {
+ // Normal routes
+ if(property_exists('\nre\configs\AppConfig', 'routes')) {
+ foreach(\nre\configs\AppConfig::$routes as &$route) {
+ $this->request->addRoute($route[0], $route[1], $route[2]);
+ }
+ }
+
+ // Reverse routes
+ if(property_exists('\nre\configs\AppConfig', 'reverseRoutes')) {
+ foreach(\nre\configs\AppConfig::$reverseRoutes as &$route) {
+ $this->request->addReverseRoute($route[0], $route[1], $route[2]);
+ }
+ }
+
+ // Revalidate request
+ $this->request->revalidate();
+ }
+
+
+ /**
+ * Handle an error that orrcurred during the
+ * loading/constructing/running of the ToplevelAgent.
+ *
+ * @param Exception $exception Occurred exception
+ * @param int $httpStatusCode HTTP-statuscode
+ */
+ private function error(\nre\core\Exception $exception, $httpStatusCode)
+ {
+ // Log error message
+ $this->log($exception, \nre\core\Logger::LOGMODE_AUTO);
+
+ try {
+ // Set agent for handling errors
+ $this->response->clearParams();
+ $this->response->addParams(
+ \nre\configs\AppConfig::$defaults['toplevel-error'],
+ \nre\configs\AppConfig::$defaults['intermediate-error'],
+ \nre\configs\CoreConfig::$defaults['action'],
+ $httpStatusCode
+ );
+
+ // Run this agent
+ parent::run();
+ }
+ catch(\nre\exceptions\ServiceUnavailableException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ActionNotFoundException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\DatamodelException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\DriverNotValidException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\DriverNotFoundException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ModelNotValidException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ModelNotFoundException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ViewNotFoundException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ControllerNotValidException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\ControllerNotFoundException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\AgentNotValidException $e) {
+ $this->errorService($e);
+ }
+ catch(\nre\exceptions\AgentNotFoundException $e) {
+ $this->errorService($e);
+ }
+ catch(Exception $e) {
+ $this->errorService($e);
+ }
+ }
+
+
+ /**
+ * Handle a error which cannot be handles by the system (and
+ * HTTP 503).
+ *
+ * $param Exception $exception Occurred exception
+ */
+ private function errorService($exception)
+ {
+ // Log error message
+ $this->log($exception, \nre\core\Logger::LOGMODE_AUTO);
+
+ // Set HTTP-rtatuscode
+ $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(503));
+
+
+ // Read and print static error file
+ $fileName = ROOT.DS.\nre\configs\CoreConfig::getClassDir('views').DS.\nre\configs\CoreConfig::$defaults['errorFile'].\nre\configs\CoreConfig::getFileExt('views');
+ ob_start();
+ include($fileName);
+ $this->response->setOutput(ob_get_clean());
+
+
+ // Prevent further execution
+ $this->response->setExit();
+ }
+
+ }
+
+?>
diff --git a/app/Controller.inc b/app/Controller.inc
new file mode 100644
index 00000000..42881662
--- /dev/null
+++ b/app/Controller.inc
@@ -0,0 +1,99 @@
+
+ * @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;
+
+
+ /**
+ * Abstract class for implementing an application Controller.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class Controller extends \nre\core\Controller
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('auth');
+ /**
+ * Linker instance
+ *
+ * @var Linker
+ */
+ protected $linker = null;
+
+
+
+
+ /**
+ * Construct a new application Controller.
+ *
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ViewNotFoundException
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ * @param Agent $agent Corresponding Agent
+ */
+ public function __construct($layoutName, $action, $agent)
+ {
+ parent::__construct($layoutName, $action, $agent);
+ }
+
+
+
+ /**
+ * Prefilter that is executed before running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Create linker
+ $this->linker = new \nre\core\Linker($this->request);
+
+ // Create date and time formatter
+ $this->set('dateFormatter', new \IntlDateFormatter(
+ \nre\core\Config::getDefault('locale'),
+ \IntlDateFormatter::MEDIUM,
+ \IntlDateFormatter::NONE,
+ NULL
+ ));
+ $this->set('timeFormatter', new \IntlDateFormatter(
+ \nre\core\Config::getDefault('locale'),
+ \IntlDateFormatter::NONE,
+ \IntlDateFormatter::SHORT,
+ NULL
+ ));
+ }
+
+
+ /**
+ * Postfilter that is executed after running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function postFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::postFilter($request, $response);
+ }
+
+ }
+
+?>
diff --git a/app/Model.inc b/app/Model.inc
new file mode 100644
index 00000000..32835d04
--- /dev/null
+++ b/app/Model.inc
@@ -0,0 +1,42 @@
+
+ * @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;
+
+
+ /**
+ * Abstract class for implementing an application Model.
+ *
+ * @author Oliver Hanraths
+ */
+ class Model extends \nre\models\DatabaseModel
+ {
+
+
+
+
+ /**
+ * Construct a new application Model.
+ *
+ * @throws DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ */
+ public function __construct()
+ {
+ parent::__construct('mysqli', \nre\configs\AppConfig::$database);
+ }
+
+ }
+
+?>
diff --git a/app/QuesttypeAgent.inc b/app/QuesttypeAgent.inc
new file mode 100644
index 00000000..23c4d1b2
--- /dev/null
+++ b/app/QuesttypeAgent.inc
@@ -0,0 +1,267 @@
+
+ * @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;
+
+
+ /**
+ * Abstract class for implementing a QuesttypeAgent.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class QuesttypeAgent extends \nre\agents\BottomlevelAgent
+ {
+ /**
+ * Current request
+ *
+ * @var Request
+ */
+ private $request;
+ /**
+ * Current response
+ *
+ * @var Response
+ */
+ private $response;
+
+
+
+
+ /**
+ * Load a QuesttypeAgent.
+ *
+ * @static
+ * @throws QuesttypeAgentNotFoundException
+ * @throws QuesttypeAgentNotValidException
+ * @param string $questtypeName Name of the QuesttypeAgent to load
+ */
+ public static function load($questtypeName)
+ {
+ // Determine full classname
+ $className = self::getClassName($questtypeName);
+
+ try {
+ // Load class
+ static::loadClass($questtypeName, $className);
+
+ // Validate class
+ static::checkClass($className, get_class());
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \hhu\z\exceptions\QuesttypeAgentNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \hhu\z\exceptions\QuesttypeAgentNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a QuesttypeAgent (Factory Pattern).
+ *
+ * @static
+ * @throws DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ * @throws QuesttypeControllerNotValidException
+ * @throws QuesttypeControllerNotFoundException
+ * @param string $questtypeName Name of the QuesttypeAgent to instantiate
+ * @param Request $request Current request
+ * @param Response $respone Current respone
+ * @param Logger $log Log-system
+ */
+ public static function factory($questtypeName, \nre\core\Request $request, \nre\core\Response $response, \nre\core\Logger $log=null)
+ {
+ // Determine full classname
+ $className = self::getClassName($questtypeName);
+
+ // Construct and return Questmodule
+ return new $className($request, $response, $log);
+ }
+
+
+ /**
+ * Determine the Agent-classname for the given Questtype-name.
+ *
+ * @static
+ * @param string $questtypeName Questtype-name to get Agent-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())), 'agent');
+
+
+ return \nre\configs\AppConfig::$app['namespace']."questtypes\\$className";
+ }
+
+
+ /**
+ * Load the class of a QuesttypeAgent.
+ *
+ * @static
+ * @throws ClassNotFoundException
+ * @param string $questtypeName Name of the QuesttypeAgent 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 QuesttypeAgent-class.
+ *
+ * @static
+ * @throws 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 QuesttypeAgent.
+ *
+ * @throws DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ * @throws QuesttypeControllerNotValidException
+ * @throws QuesttypeControllerNotFoundException
+ * @param Request $request Current request
+ * @param Response $respone Current response
+ * @param Logger $log Log-system
+ */
+ protected function __construct(\nre\core\Request $request, \nre\core\Response $response, \nre\core\Logger $log=null)
+ {
+ // Store values
+ $this->request = $request;
+ $this->response = $response;
+
+
+ // Call parent constructor
+ parent::__construct($request, $response, $log);
+ }
+
+
+
+
+ /**
+ * Save the answers of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ $this->controller->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers);
+ }
+
+
+ /**
+ * Check if answers of a Character for a Quest match the correct ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ return $this->controller->matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers);
+ }
+
+
+
+
+ /**
+ * Load the Controller of this Agent.
+ *
+ * @throws DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ * @throws QuesttypeControllerNotValidException
+ * @throws QuesttypeControllerNotFoundException
+ */
+ protected function loadController()
+ {
+ // Determine Controller name
+ $controllerName = \nre\core\ClassLoader::stripClassType(\nre\core\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
+ \hhu\z\QuesttypeController::load($controllerName);
+
+ // Construct Controller
+ $this->controller = QuesttypeController::factory($controllerName, $toplevelAgentName, $action, $this);
+ }
+
+ }
+
+?>
diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc
new file mode 100644
index 00000000..6bbabdd2
--- /dev/null
+++ b/app/QuesttypeController.inc
@@ -0,0 +1,348 @@
+
+ * @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;
+
+
+ /**
+ * Abstract class for implementing a QuesttypeController.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class QuesttypeController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroups', 'quests', 'characters');
+
+
+
+
+ /**
+ * Save the answers of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public abstract function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers);
+
+
+ /**
+ * Check if answers of a Character for a Quest match the correct ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ * @return boolean True/false for a right/wrong answer or null for moderator evaluation
+ */
+ public abstract function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers);
+
+
+ /**
+ * Action: quest.
+ *
+ * Show the task of a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public abstract function quest($seminary, $questgroup, $quest, $character);
+
+
+
+ /**
+ * Action: submission.
+ *
+ * Show the submission of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public abstract function submission($seminary, $questgroup, $quest, $character);
+
+
+
+
+ /**
+ * Load a QuesttypeController.
+ *
+ * @static
+ * @throws QuesttypeControllerNotFoundException
+ * @throws QuesttypeControllerNotValidException
+ * @param string $controllerName Name of the QuesttypeController to load
+ */
+ public static function load($controllerName)
+ {
+ // Determine full classname
+ $className = self::getClassName($controllerName);
+
+ try {
+ // Load class
+ static::loadClass($controllerName, $className);
+
+ // Validate class
+ static::checkClass($className, get_class());
+ }
+ catch(\nre\exceptions\ClassNotValidException $e) {
+ throw new \hhu\z\exceptions\QuesttypeControllerNotValidException($e->getClassName());
+ }
+ catch(\nre\exceptions\ClassNotFoundException $e) {
+ throw new \hhu\z\exceptions\QuesttypeControllerNotFoundException($e->getClassName());
+ }
+ }
+
+
+ /**
+ * Instantiate a QuesttypeController (Factory Pattern).
+ *
+ * @static
+ * @throws DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ * @throws ViewNotFoundException
+ * @param string $controllerName Name of the QuesttypeController to instantiate
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ */
+ 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 Controller-classname for the given Questtype-name.
+ *
+ * @static
+ * @param string $questtypeName Questtype-name to get Controller-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())), 'controller');
+
+
+ return \nre\configs\AppConfig::$app['namespace']."questtypes\\$className";
+ }
+
+
+ /**
+ * Load the class of a QuesttypeController
+ *
+ * @static
+ * @throws ClassNotFoundException
+ * @param string $questtypeName Name of the QuesttypeController 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 QuesttypeController-class.
+ *
+ * @static
+ * @throws 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 application Controller.
+ *
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ * @throws ViewNotFoundException
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ * @param Agent $agent Corresponding Agent
+ */
+ public function __construct($layoutName, $action, $agent)
+ {
+ parent::__construct($layoutName, $action, $agent);
+ }
+
+
+
+
+ /**
+ * Load the Models of this Controller.
+ *
+ * @throws DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ */
+ protected function loadModels()
+ {
+ // Load default models
+ parent::loadModels();
+
+ // Load QuesttypeModel
+ $this->loadModel();
+ }
+
+
+ /**
+ * Load the Model of the Questtype.
+ *
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ */
+ private function loadModel()
+ {
+ // Determine Model
+ $model = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))));
+
+ // Load class
+ QuesttypeModel::load($model);
+
+ // Construct Model
+ $modelName = ucfirst(strtolower($model));
+ $this->$modelName = QuesttypeModel::factory($model);
+ }
+
+
+ /**
+ * Load the View of this QuesttypeController.
+ *
+ * @throws 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::stripClassType(\nre\core\ClassLoader::getClassName(get_class($this)));
+
+
+ // Load view
+ $this->view = QuesttypeView::loadAndFactory($layoutName, $controllerName, $action);
+ }
+
+
+ /**
+ * Mark the current Quest as solved and redirect to solved page.
+ */
+ protected function setQuestSolved()
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3));
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $this->request->getParam(4));
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $this->request->getParam(5));
+
+ // Character
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+
+ // Set solved
+ $this->Quests->setQuestSolved($quest['id'], $character['id']);
+
+
+ // Redirect
+ $this->redirect($this->linker->link('Epilog', $sidequest != null ? 6 : 5, true, array('status'=>'solved')));
+ }
+
+
+ /**
+ * Mark the current Quest as unsolved and redirect to unsolved
+ * page.
+ */
+ protected function setQuestUnsolved()
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3));
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $this->request->getParam(4));
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $this->request->getParam(5));
+
+ // Character
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+
+ // Set solved
+ $this->Quests->setQuestUnsolved($quest['id'], $character['id']);
+
+
+ // Redirect
+ $this->redirect($this->linker->link('Prolog', $sidequest != null ? 6 : 5, true, array('status'=>'unsolved')));
+ }
+
+ }
+
+?>
diff --git a/app/QuesttypeModel.inc b/app/QuesttypeModel.inc
new file mode 100644
index 00000000..d30699fb
--- /dev/null
+++ b/app/QuesttypeModel.inc
@@ -0,0 +1,154 @@
+
+ * @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;
+
+
+ /**
+ * Abstract class for implementing a QuesttypeModel.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class QuesttypeModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Load a Model.
+ *
+ * @static
+ * @throws QuesttypeModelNotFoundException
+ * @throws 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 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 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 DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws QuesttypeModelNotValidException
+ * @throws QuesttypeModelNotFoundException
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ }
+
+?>
diff --git a/app/QuesttypeView.inc b/app/QuesttypeView.inc
new file mode 100644
index 00000000..8e77ee00
--- /dev/null
+++ b/app/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;
+
+
+ /**
+ * Abstract class for implementing a QuesttypeView.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeView extends \nre\core\View
+ {
+
+
+
+
+ /**
+ * Load and instantiate the QuesttypeView of a QuesttypeAgent.
+ *
+ * @throws 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 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/ToplevelAgent.inc b/app/ToplevelAgent.inc
new file mode 100644
index 00000000..97aea57b
--- /dev/null
+++ b/app/ToplevelAgent.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;
+
+
+ /**
+ * Abstract class for implementing an application Controller.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class ToplevelAgent extends \nre\agents\ToplevelAgent
+ {
+
+
+
+
+ protected function __construct(\nre\core\Request $request, \nre\core\Response $response, \nre\core\Logger $log=null)
+ {
+ parent::__construct($request, $response, $log);
+
+
+ // Set timezone
+ date_default_timezone_set(\nre\configs\AppConfig::$app['timeZone']);
+ }
+ }
+
+?>
diff --git a/app/Utils.inc b/app/Utils.inc
new file mode 100644
index 00000000..d248dee1
--- /dev/null
+++ b/app/Utils.inc
@@ -0,0 +1,83 @@
+
+ * @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;
+
+
+ /**
+ * Class for implementing utility methods.
+ *
+ * @author Oliver Hanraths
+ */
+ class Utils
+ {
+
+
+ /**
+ * Mask HTML-chars for save output.
+ *
+ * @static
+ * @param string $string String to be masked
+ * @return string Masked string
+ */
+ static function t($string)
+ {
+ return nl2br(htmlspecialchars($string));
+ }
+
+
+ /**
+ * ‚htmlspecialchars‘ with support for UTF-8.
+ *
+ * @static
+ * @param string $string String to be masked
+ * @return string Masked string
+ */
+ public static function htmlspecialchars_utf8($string)
+ {
+ return htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
+ }
+
+
+ /**
+ * Cut a string to the given length but only word boundaries.
+ *
+ * @static
+ * @param string $string String to cut
+ * @param int $length Length to cut string
+ * @param int $scope Maximum length to cut string regardless word boundaries
+ * @return string Cutted string
+ */
+ public static function shortenString($string, $length, $scope)
+ {
+ // Determine length
+ $length = min($length, strlen($string));
+
+ // Look for word boundary
+ if(($pos = strpos($string, ' ', $length)) !== false)
+ {
+ // Check if boundary is outside of scope
+ if($pos > $length + $scope) {
+ $pos = strrpos(substr($string, 0, $pos), ' ');
+ }
+ }
+ else {
+ $pos = strlen($string);
+ }
+
+
+ // Cut string and return it
+ return substr($string, 0, $pos);
+ }
+
+ }
+
+?>
diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc
new file mode 100644
index 00000000..e06a9a03
--- /dev/null
+++ b/app/controllers/SeminaryRoleController.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;
+
+
+ /**
+ * Abstract class for implementing a Controller for a Seminary and its
+ * concepts.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class SeminaryRoleController extends \hhu\z\Controller
+ {
+ /**
+ * Required components
+ *
+ * @var array
+ */
+ public $components = array('achievement', 'auth');
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'userseminaryroles', 'characters', 'achievements');
+ /**
+ * Data of currently logged in user if any
+ *
+ * @var array
+ */
+ public static $user = null;
+
+
+
+
+ /**
+ * Construct a new SeminaryRole Controller.
+ *
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ViewNotFoundException
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ * @param Agent $agent Corresponding Agent
+ */
+ public function __construct($layoutName, $action, $agent)
+ {
+ parent::__construct($layoutName, $action, $agent);
+ }
+
+
+
+ /**
+ * Prefilter that is executed before running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Check permissions
+ $this->checkPermission($request, $response);
+ }
+
+
+ /**
+ * Postfilter that is executed after running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function postFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::postFilter($request, $response);
+ }
+
+
+
+
+ /**
+ * Check user permissions.
+ *
+ * @throws AccessDeniedException
+ */
+ private function checkPermission(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ // Do not check index page
+ if(is_null($request->getParam(3))) {
+ return;
+ }
+
+ // Determine user and seminary
+ $userId = $this->Auth->getUserId();
+ $seminary = $this->Seminaries->getSeminaryByUrl($request->getParam(3));
+
+ // Determine user seminary roles
+ $userSeminaryRoles = array();
+ $roles = $this->Userseminaryroles->getUserseminaryrolesForUserById($userId, $seminary['id']);
+ foreach($roles as &$role) {
+ $userSeminaryRoles[] = $role['name'];
+ }
+
+
+ // Determine permissions for current action
+ $action = $this->request->getParam(2, 'action');
+ if(!property_exists($this, 'seminaryPermissions')) {
+ return; // Allow if nothing is specified
+ }
+ if(!array_key_exists($action, $this->seminaryPermissions)) {
+ return; // Allow if Action is not specified
+ }
+ $permissions = $this->seminaryPermissions[$action];
+
+
+ // Check permissions
+ if(count(array_intersect($userSeminaryRoles, $permissions)) == 0) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+
+ }
+
+?>
diff --git a/app/controllers/ToplevelController.inc b/app/controllers/ToplevelController.inc
new file mode 100644
index 00000000..30558c91
--- /dev/null
+++ b/app/controllers/ToplevelController.inc
@@ -0,0 +1,170 @@
+
+ * @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;
+
+
+ /**
+ * Abstract class for implementing a Controller of a ToplevelAgent.
+ *
+ * @author Oliver Hanraths
+ */
+ abstract class ToplevelController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('users', 'userroles', 'seminaries', 'characters');
+ /**
+ * Current user
+ *
+ * @var array
+ */
+ public static $user = null;
+ /**
+ *
+ */
+ public static $seminary = null;
+ /**
+ * Character of current user and Seminary
+ *
+ * @var array
+ */
+ public static $character = null;
+
+
+
+
+ /**
+ * Construct a new application Controller.
+ *
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ViewNotFoundException
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ * @param Agent $agent Corresponding Agent
+ */
+ public function __construct($layoutName, $action, $agent)
+ {
+ parent::__construct($layoutName, $action, $agent);
+ }
+
+
+
+ /**
+ * Prefilter that is executed before running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Get userdata
+ try {
+ static::$user = $this->Users->getUserById($this->Auth->getUserId());
+
+ // Character
+ $controller = $this->agent->getIntermediateAgent()->controller;
+ if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController'))
+ {
+ $seminaryUrl = $this->request->getParam(3);
+ static::$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+ static::$character = $this->Characters->getCharacterForUserAndSeminary(static::$user['id'], static::$seminary['id']);
+ }
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ }
+
+ // Check permissions
+ $this->checkPermission($request, $response);
+
+ // Set userdata
+ $this->set('loggedUser', static::$user);
+ $this->set('loggedSeminary', static::$seminary);
+ $this->set('loggedCharacter', static::$character);
+ }
+
+
+ /**
+ * Postfilter that is executed after running the Controller.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function postFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::postFilter($request, $response);
+ }
+
+
+
+
+ /**
+ * Check user permissions.
+ *
+ * @throws AccessDeniedException
+ */
+ private function checkPermission(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ // Determine user
+ $userId = $this->Auth->getUserId();
+
+
+ // Do not check error pages
+ if($response->getParam(0, 'toplevel') == \nre\core\Config::getDefault('toplevel-error')) {
+ return;
+ }
+ if($response->getParam(1, 'intermediate') == \nre\core\Config::getDefault('intermediate-error')) {
+ return;
+ }
+
+
+ // Determine user roles
+ if($userId > 0)
+ {
+ $userRoles = array();
+ $roles = $this->Userroles->getUserrolesForUserById($userId);
+ foreach($roles as &$role) {
+ $userRoles[] = $role['name'];
+ }
+ }
+ else {
+ $userRoles = array('guest');
+ }
+
+ // Determine permissions of Intermediate Controller for current action
+ $controller = $this->agent->getIntermediateAgent()->controller;
+ $action = $this->request->getParam(2, 'action');
+ if(!property_exists($controller, 'permissions')) {
+ return; // Allow if nothing is specified
+ }
+ if(!array_key_exists($action, $controller->permissions)) {
+ return; // Allow if Action is not specified
+ }
+ $permissions = $controller->permissions[$action];
+
+
+ // Check permissions
+ if(count(array_intersect($userRoles, $permissions)) == 0) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+
+ }
+
+?>
diff --git a/app/exceptions/QuesttypeAgentNotFoundException.inc b/app/exceptions/QuesttypeAgentNotFoundException.inc
new file mode 100644
index 00000000..5f6f4ea9
--- /dev/null
+++ b/app/exceptions/QuesttypeAgentNotFoundException.inc
@@ -0,0 +1,77 @@
+
+ * @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\exceptions;
+
+
+ /**
+ * Exception: QuesttypeAgent not found.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeAgentNotFoundException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 101;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'QuesttypeAgent not found';
+
+ /**
+ * Name of the class that was not found
+ *
+ * @var string
+ */
+ private $questtypeName;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $questtypeName Name of the QuesttypeAgent that was not found
+ */
+ function __construct($questtypeName, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $questtypeName
+ );
+
+ // Store values
+ $this->questtypeName = $questtypeName;
+ }
+
+
+
+
+ /**
+ * Get the name of the QuesttypeAgent that was not found.
+ *
+ * @return string Name of the QuesttypeAgent that was not found
+ */
+ public function getClassName()
+ {
+ return $this->questtypeName;
+ }
+
+ }
+
+?>
diff --git a/app/exceptions/QuesttypeAgentNotValidException.inc b/app/exceptions/QuesttypeAgentNotValidException.inc
new file mode 100644
index 00000000..8fa7237f
--- /dev/null
+++ b/app/exceptions/QuesttypeAgentNotValidException.inc
@@ -0,0 +1,77 @@
+
+ * @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\exceptions;
+
+
+ /**
+ * Exception: QuesttypeAgent not valid.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeAgentNotValidException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 102;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'QuesttypeAgent not valid';
+
+ /**
+ * Name of the invalid class
+ *
+ * @var string
+ */
+ private $questtypeName;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $questtypeName Name of the invalid QuesttypeAgent
+ */
+ function __construct($questtypeName, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $questtypeName
+ );
+
+ // Store value
+ $this->questtypeName = $questtypeName;
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid QuesttypeAgent.
+ *
+ * @return string Name of the invalid QuesttypeAgent
+ */
+ public function getClassName()
+ {
+ return $this->questtypeName;
+ }
+
+ }
+
+?>
diff --git a/app/exceptions/QuesttypeControllerNotFoundException.inc b/app/exceptions/QuesttypeControllerNotFoundException.inc
new file mode 100644
index 00000000..6d3a4a36
--- /dev/null
+++ b/app/exceptions/QuesttypeControllerNotFoundException.inc
@@ -0,0 +1,77 @@
+
+ * @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\exceptions;
+
+
+ /**
+ * Exception: QuesttypeController not found.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeControllerNotFoundException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 103;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'QuesttypeController not found';
+
+ /**
+ * Name of the class that was not found
+ *
+ * @var string
+ */
+ private $questtypeName;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $questtypeName Name of the QuesttypeController that was not found
+ */
+ function __construct($questtypeName, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $questtypeName
+ );
+
+ // Store values
+ $this->questtypeName = $questtypeName;
+ }
+
+
+
+
+ /**
+ * Get the name of the QuesttypeController that was not found.
+ *
+ * @return string Name of the QuesttypeController that was not found
+ */
+ public function getClassName()
+ {
+ return $this->questtypeName;
+ }
+
+ }
+
+?>
diff --git a/app/exceptions/QuesttypeControllerNotValidException.inc b/app/exceptions/QuesttypeControllerNotValidException.inc
new file mode 100644
index 00000000..8c6735b8
--- /dev/null
+++ b/app/exceptions/QuesttypeControllerNotValidException.inc
@@ -0,0 +1,77 @@
+
+ * @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\exceptions;
+
+
+ /**
+ * Exception: QuesttypeController not valid.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeControllerNotValidException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 104;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'QuesttypeController not valid';
+
+ /**
+ * Name of the invalid class
+ *
+ * @var string
+ */
+ private $questtypeName;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $questtypeName Name of the invalid QuesttypeController
+ */
+ function __construct($questtypeName, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $questtypeName
+ );
+
+ // Store value
+ $this->questtypeName = $questtypeName;
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid QuesttypeController.
+ *
+ * @return string Name of the invalid QuesttypeController
+ */
+ public function getClassName()
+ {
+ return $this->questtypeName;
+ }
+
+ }
+
+?>
diff --git a/app/exceptions/QuesttypeModelNotFoundException.inc b/app/exceptions/QuesttypeModelNotFoundException.inc
new file mode 100644
index 00000000..a2aa01c8
--- /dev/null
+++ b/app/exceptions/QuesttypeModelNotFoundException.inc
@@ -0,0 +1,77 @@
+
+ * @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\exceptions;
+
+
+ /**
+ * Exception: QuesttypeModel not found.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeModelNotFoundException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 105;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'QuesttypeModel not found';
+
+ /**
+ * Name of the class that was not found
+ *
+ * @var string
+ */
+ private $questtypeName;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $questtypeName Name of the QuesttypeModel that was not found
+ */
+ function __construct($questtypeName, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $questtypeName
+ );
+
+ // Store values
+ $this->questtypeName = $questtypeName;
+ }
+
+
+
+
+ /**
+ * Get the name of the QuesttypeModel that was not found.
+ *
+ * @return string Name of the QuesttypeModel that was not found
+ */
+ public function getClassName()
+ {
+ return $this->questtypeName;
+ }
+
+ }
+
+?>
diff --git a/app/exceptions/QuesttypeModelNotValidException.inc b/app/exceptions/QuesttypeModelNotValidException.inc
new file mode 100644
index 00000000..4a78beb6
--- /dev/null
+++ b/app/exceptions/QuesttypeModelNotValidException.inc
@@ -0,0 +1,77 @@
+
+ * @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\exceptions;
+
+
+ /**
+ * Exception: QuesttypeModel not valid.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypeModelNotValidException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 106;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'QuesttypeModel not valid';
+
+ /**
+ * Name of the invalid class
+ *
+ * @var string
+ */
+ private $questtypeName;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $questtypeName Name of the invalid QuesttypeModel
+ */
+ function __construct($questtypeName, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $questtypeName
+ );
+
+ // Store value
+ $this->questtypeName = $questtypeName;
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid QuesttypeModel.
+ *
+ * @return string Name of the invalid QuesttypeModel
+ */
+ public function getClassName()
+ {
+ return $this->questtypeName;
+ }
+
+ }
+
+?>
diff --git a/app/lib/Password.inc b/app/lib/Password.inc
new file mode 100644
index 00000000..f9acf7d5
--- /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/bootstrap.inc b/bootstrap.inc
new file mode 100644
index 00000000..55647ac0
--- /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..9077da8f
--- /dev/null
+++ b/configs/AppConfig.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 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' => 'The Legend of Z',
+ 'namespace' => 'hhu\\z\\',
+ 'timeZone' => 'Europe/Berlin'
+ );
+
+
+ /**
+ * Default values
+ *
+ * @static
+ * @var array
+ */
+ public static $defaults = array(
+ 'toplevel' => 'html',
+ 'toplevel-error' => 'fault',
+ 'intermediate' => 'introduction',
+ 'intermediate-error' => 'error',
+ 'language' => 'de_DE.utf8',
+ 'locale' => 'de-DE'
+ );
+
+
+ /**
+ * Directories
+ *
+ * @static
+ * @var array
+ */
+ public static $dirs = array(
+ 'locale' => 'locale',
+ 'media' => 'media',
+ 'questtypes' => 'questtypes',
+ 'temporary' => 'tmp'
+ );
+
+
+ /**
+ * Routes
+ *
+ * @static
+ * @var array
+ */
+ public static $routes = array(
+ array('css/?(.*)', 'css/$1?layout=stylesheet', true),
+ array('users/([^/]+)/(edit|delete)', 'users/$2/$1', true),
+ array('users/(?!(index|login|logout|create|edit|delete))', 'users/user/$1', true),
+ array('seminaries/([^/]+)/(edit|delete)', 'seminaries/$2/$1', true),
+ array('seminaries/(?!(index|create|edit|delete))', 'seminaries/seminary/$1', true),
+ /*// z/ ⇒ z/seminaries/seminary/
+ array('^([^/]+)/*$', 'seminaries/seminary/$1', true),
+ // z// ⇒ z/questgroups/questgroup//
+ array('^([^/]+)/([^/]+)/?$', 'questgropus/questgroup/$1/$2', true),
+ // z/// ⇒ z/quests/quest///
+ array('^([^/]+)/([^/]+)/([^/]+)/?$', 'quests/quest/$1/$2/3', true)*/
+ array('characters/(?!(index|character))', 'characters/index/$1', true),
+ array('charactergroups/(?!(index|groupsgroup|group))', 'charactergroups/index/$1', true),
+ array('charactergroupsquests/(?!(quest))', 'charactergroupsquests/quest/$1', true),
+ array('media/(.*)', 'media/$1?layout=binary', false)
+ );
+
+
+ /**
+ * Reverse routes
+ *
+ * @static
+ * @var array
+ */
+ public static $reverseRoutes = array(
+ array('users/user/(.*)', 'users/$1', true),
+ array('users/([^/]+)/(.*)', 'users/$2/$1', true),
+ array('seminaries/seminary/(.*)', 'seminaries/$1', false),
+ //array('seminaries/seminary/(.*)', '$1', false)
+ array('characters/index/(.*)', 'characters/$1', true),
+ array('charactergroup/index/(.*)', 'charactergroup/$1', true),
+ array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true)
+ );
+
+
+ /**
+ * Database connection settings
+ *
+ * @static
+ * @var array
+ */
+ public static $database = array(
+ 'user' => 'z',
+ 'host' => 'localhost',
+ 'password' => 'legendofZ',
+ 'db' => 'z'
+ );
+
+ }
+
+?>
diff --git a/configs/CoreConfig.inc b/configs/CoreConfig.inc
new file mode 100644
index 00000000..45bc7e4a
--- /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/BinaryController.inc b/controllers/BinaryController.inc
new file mode 100644
index 00000000..09fee065
--- /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\ToplevelController
+ {
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Create binary data.
+ */
+ public function index()
+ {
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc
new file mode 100644
index 00000000..1028d28c
--- /dev/null
+++ b/controllers/CharactergroupsController.inc
@@ -0,0 +1,143 @@
+
+ * @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\SeminaryRoleController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'charactergroups', 'charactergroupsquests');
+ /**
+ * 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: index.
+ *
+ * Show Character groups-groups for a Seminary.
+ *
+ * @throws 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']);
+
+
+ // 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 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']);
+
+ // Get Character groups-group Quests
+ $quests = $this->Charactergroupsquests->getQuestsForCharactergroupsgroup($groupsgroup['id']);
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('groups', $groups);
+ $this->set('quests', $quests);
+ }
+
+
+ /**
+ * Action: group.
+ *
+ * Show a Character group for a Character groups-group of a
+ * Seminary.
+ *
+ * @throws 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);
+
+ // Get Characters
+ $characters = $this->Characters->getCharactersForGroup($group['id']);
+
+ // Get Character groups Quests
+ $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']);
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('group', $group);
+ $this->set('characters', $characters);
+ $this->set('quests', $quests);
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc
new file mode 100644
index 00000000..52dc3435
--- /dev/null
+++ b/controllers/CharactergroupsquestsController.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 the CharactergroupsquestsAgent to display Character
+ * groups Quests.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsquestsController extends \hhu\z\controllers\SeminaryRoleController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'charactergroups', 'charactergroupsquests', 'media');
+ /**
+ * 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 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 Character groups-groups
+ $groups = $this->Charactergroupsquests->getGroupsForQuest($quest['id']);
+
+ // Media
+ $questmedia = null;
+ if(!is_null($quest['questsmedia_id'])) {
+ $questmedia = $this->Media->getMediaById($quest['questsmedia_id']);
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('groupsgroup', $groupsgroup);
+ $this->set('quest', $quest);
+ $this->set('groups', $groups);
+ $this->set('media', $questmedia);
+ }
+
+ }
+
+?>
diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc
new file mode 100644
index 00000000..e2e56b64
--- /dev/null
+++ b/controllers/CharactersController.inc
@@ -0,0 +1,111 @@
+
+ * @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\SeminaryRoleController
+ {
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator'),
+ 'character' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'seminarycharacterfields');
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List registered Characters for a Seminary
+ *
+ * @throws IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a Seminary
+ */
+ public function index($seminaryUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get registered Characters
+ $characters = $this->Characters->getCharactersForSeminary($seminary['id']);
+
+ // Additional Character information
+ foreach($characters as &$character)
+ {
+ // Level
+ $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']);
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('characters', $characters);
+ }
+
+
+ /**
+ * Action: character.
+ *
+ * Show a Charater and its details.
+ *
+ * @throws 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);
+
+ // Get Character
+ $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl);
+
+ // Character Level
+ $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']);
+
+ // Get Seminarycharacterfields
+ $characterfields = $this->Seminarycharacterfields->getFieldsForCharacter($character['id']);
+
+ // Get User
+ $user = $this->Users->getUserById($character['user_id']);
+
+ // Get Character groups
+ $groups = $this->Charactergroups->getGroupsForCharacter($character['id']);
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('character', $character);
+ $this->set('characterfields', $characterfields);
+ $this->set('user', $user);
+ $this->set('groups', $groups);
+ }
+
+ }
+
+?>
diff --git a/controllers/ErrorController.inc b/controllers/ErrorController.inc
new file mode 100644
index 00000000..efdae4f4
--- /dev/null
+++ b/controllers/ErrorController.inc
@@ -0,0 +1,48 @@
+
+ * @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]);
+ }
+
+ }
+
+?>
diff --git a/controllers/FaultController.inc b/controllers/FaultController.inc
new file mode 100644
index 00000000..be01fea7
--- /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..20795d5b
--- /dev/null
+++ b/controllers/HtmlController.inc
@@ -0,0 +1,59 @@
+
+ * @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\controllers\ToplevelController
+ {
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param Request $request Current request
+ * @param 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");
+ }
+
+
+ /**
+ * 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', static::$user);
+ $this->set('loggedSeminary', static::$seminary);
+ $this->set('loggedCharacter', static::$character);
+ }
+
+ }
+
+?>
diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc
new file mode 100644
index 00000000..5fead9cf
--- /dev/null
+++ b/controllers/IntroductionController.inc
@@ -0,0 +1,35 @@
+
+ * @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\Controller
+ {
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index()
+ {
+ }
+
+ }
+
+?>
diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc
new file mode 100644
index 00000000..897f2701
--- /dev/null
+++ b/controllers/MediaController.inc
@@ -0,0 +1,240 @@
+
+ * @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\SeminaryRoleController
+ {
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'index' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'media');
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param Request $request Current request
+ * @param 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: index.
+ *
+ * Display a medium without processing.
+ *
+ * @throws IdNotFoundException
+ * @param string $seminaryUrl URL-title of the Seminary
+ * @param string $mediaUrl URL-name of the medium
+ */
+ public function index($seminaryUrl, $mediaUrl, $action=null)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Media
+ $media = $this->Media->getMediaByUrl($seminary['id'], $mediaUrl);
+
+ // 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['media'].DS.$media['id'];
+ if(!file_exists($media['filename'])) {
+ throw new \nre\exceptions\IdNotFoundException($mediaUrl);
+ }
+
+ // Cache
+ if($this->setCacheHeaders($media['filename'])) {
+ return;
+ }
+
+ // Load and process file
+ $file = null;
+ switch($action)
+ {
+ // No action
+ case null:
+ // Do not process the file
+ $file = file_get_contents($media['filename']);
+ break;
+ case 'questgroup':
+ if(!in_array(strtoupper($format), static::getImageTypes())) {
+ $file = file_get_contents($media['filename']);
+ }
+ else
+ {
+ $file = static::resizeImage(
+ $media['filename'],
+ $format,
+ 480
+ );
+ }
+ break;
+ default:
+ throw new ParamsNotValidException($action);
+ break;
+ }
+
+
+
+ // 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) && $fileLastModified < strtotime($headerModifiedSince) &&
+ !is_null($headerNoneMatch) && $headerNoneMatch == $fileEtag
+ ) {
+ $this->response->setExit(true);
+ $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304));
+
+ return true;
+ }
+
+
+ return false;
+ }
+
+
+
+
+ /**
+ * 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 $size New size to resize to
+ */
+ private static function resizeImage($fileName, $mimeType, $size)
+ {
+ // Read image from cache
+ $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($fileName).'-'.$size;
+ 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($fileName);
+
+ // Calculate new size
+ $geometry = $im->getImageGeometry();
+ if($geometry['width'] < $size) {
+ $size = $geometry['width'];
+ }
+
+ // Process
+ $im->thumbnailImage($size, 5000, true);
+ $im->contrastImage(1);
+ $im->setImageFormat($mimeType);
+
+ // Save temporary file
+ $im->writeImage($tempFileName);
+
+
+ // Return resized image
+ return $im;
+ }
+
+ }
+
+?>
diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc
new file mode 100644
index 00000000..3d9d5551
--- /dev/null
+++ b/controllers/MenuController.inc
@@ -0,0 +1,50 @@
+
+ * @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
+ {
+
+
+
+
+ /**
+ * Prefilter.
+ *
+ * @param Request $request Current request
+ * @param Response $response Current response
+ */
+ public function preFilter(\nre\core\Request $request, \nre\core\Response $response)
+ {
+ parent::preFilter($request, $response);
+
+ // Set userdata
+ $this->set('loggedUser', HtmlController::$user);
+ }
+
+
+ /**
+ * Action: index.
+ */
+ public function index()
+ {
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc
new file mode 100644
index 00000000..8657bf89
--- /dev/null
+++ b/controllers/QuestgroupsController.inc
@@ -0,0 +1,138 @@
+
+ * @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\SeminaryRoleController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroupshierarchy', 'questgroups', 'quests');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'questgroup' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'questgroup' => array('admin', 'moderator', 'user')
+ );
+
+
+
+
+ /**
+ * Action: questgroup.
+ *
+ * Display a Questgroup and its data.
+ *
+ * @throws 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
+ $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyById($questgroup['questgroupshierarchy_id']);
+
+ // Get Character
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+
+ // Check permission
+ $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($questgroup['id']);
+ if(!is_null($previousQuestgroup)) {
+ if(!$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+
+ // Get child Questgroupshierarchy
+ $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupshierarchy['id']);
+ foreach($childQuestgroupshierarchy as &$hierarchy)
+ {
+ // Get Questgroups
+ $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']);
+
+ // Get additional data
+ for($i=0; $iQuestgroups->getAchievedXPsForQuestgroup($hierarchy['questgroups'][$i]['id'], $character['id']);
+
+ // Check permission of Questgroups
+ if($i >= 1) {
+ $hierarchy['questgroups'][$i]['access'] = $this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']);
+ }
+ }
+ }
+
+ // Get texts
+ $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']);
+
+ // Get Character XPs
+ $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']);
+
+
+ // Get Quests
+ $quests = null;
+ if(count($childQuestgroupshierarchy) == 0)
+ {
+ $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']);
+ foreach($quests as $i => &$quest)
+ {
+ // Set status
+ $quest['solved'] = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+ // Check permission
+ if($i > 0) {
+ $quest['access'] = $this->Quests->hasCharacterSolvedQuest($quests[$i-1]['id'], $character['id']);
+ }
+
+ // Attach sidequests
+ $quests[$i]['sidequests'] = $this->Quests->getSidequestsForQuest($quest['id']);
+ }
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('questgroupshierarchy', $questgroupshierarchy);
+ $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy);
+ $this->set('texts', $questgroupTexts);
+ $this->set('quests', $quests);
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc
new file mode 100644
index 00000000..e746d198
--- /dev/null
+++ b/controllers/QuestgroupshierarchypathController.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\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');
+
+
+
+
+ /**
+ * 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);
+
+ // Get parent Questgrouphierarchy
+ $parentQuestgroupshierarchy = array();
+ $currentQuestgroup = $questgroup;
+ if($showGroup) {
+ $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyById($currentQuestgroup['questgroupshierarchy_id']);
+ array_unshift($parentQuestgroupshierarchy, $currentQuestgroup);
+ }
+ while(!is_null($currentQuestgroup['parent_questgroup_id']))
+ {
+ // Get Questgroup
+ $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['parent_questgroup_id']);
+
+ // Get Questgroupshierarchy
+ $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyById($currentQuestgroup['questgroupshierarchy_id']);
+
+ array_unshift($parentQuestgroupshierarchy, $currentQuestgroup);
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy);
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestgroupspictureController.inc b/controllers/QuestgroupspictureController.inc
new file mode 100644
index 00000000..7810869b
--- /dev/null
+++ b/controllers/QuestgroupspictureController.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 QuestgroupspictureAgent to display the picture of a
+ * Questgroups.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupspictureController extends \hhu\z\Controller
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroups', 'media');
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * Show the picture of a Questgroup.
+ *
+ * @param string $seminaryUrl URL-Title of a Seminary
+ * @param string $questgroupUrl URL-Title of a Questgroup
+ */
+ public function index($seminaryUrl, $questgroupUrl)
+ {
+ // Get Seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Get Questgroup
+ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl);
+
+ // Get Picture
+ $picture = null;
+ try {
+ $picture = $this->Media->getMediaById($questgroup['questgroupspicture_id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('picture', $picture);
+ }
+
+ }
+
+?>
diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc
new file mode 100644
index 00000000..3204bb7c
--- /dev/null
+++ b/controllers/QuestsController.inc
@@ -0,0 +1,558 @@
+
+ * @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\SeminaryRoleController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'questgroups', 'quests', 'questtexts', 'media', 'questtypes', 'questgroupshierarchy');
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'quest' => array('admin', 'moderator', 'user'),
+ 'submissions' => array('admin', 'moderator'),
+ 'submission' => array('admin', 'moderator')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'quest' => array('admin', 'moderator', 'user'),
+ 'submissions' => array('admin', 'moderator'),
+ 'submission' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: quest.
+ *
+ * Show a quest and its task.
+ *
+ * @throws 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);
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Get Character
+ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']);
+
+ // Check permissions
+ $previousQuests = $this->Quests->getPreviousQuests($quest['id']);
+ if(count($previousQuests) == 0)
+ {
+ // Previous Questgroup
+ $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($questgroup['id']);
+ if(!is_null($previousQuestgroup) && !$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ else
+ {
+ // Previous Quests
+ if(count($previousQuests) > 0)
+ {
+ $solved = false;
+ foreach($previousQuests as &$previousQuest)
+ {
+ if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) {
+ $solved = true;
+ break;
+ }
+ }
+ if(!$solved) {
+ throw new \nre\exceptions\AccessDeniedException();
+ }
+ }
+ }
+
+ // Get (related) Questtext (for Sidequests)
+ $relatedQuesttext = null;
+ if(!$quest['is_mainquest'])
+ {
+ $relatedQuesttext = $this->Questtexts->getQuesttextForSidequest($quest['id']);
+ if(!empty($relatedQuesttext)) {
+ $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']);
+ }
+ }
+
+ // Get Questtext
+ $questtext = null;
+ if(is_null($questtexttypeUrl)) {
+ $questtexttypeUrl = 'Prolog';
+ }
+ $questtexttypes = $this->Questtexts->getQuesttexttypes();
+ $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes);
+ $questtextCount = $this->Questtexts->getQuesttextsCountForQuest($quest['id'], $questtexttypeUrl);
+ if(/*$questtextCount > 0 && */in_array($questtexttypeUrl, $questtexttypes))
+ {
+ $questtextPos = max(intval($questtextPos), 1);
+ try {
+ $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos);
+ $questtext['count'] = $questtextCount;
+ $questtext['sidequests'] = $this->Quests->getSidequestsForQuesttext($questtext['id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ if(!($questtexttypeUrl == 'Prolog' || $questtexttypeUrl == 'Epilog' && $questtextPos == 1)) {
+ throw $e;
+ }
+ }
+ }
+
+ // Quest status
+ $questStatus = $this->request->getGetParam('status');
+ $questStatusText = null;
+ if(!is_null($questStatus))
+ {
+ switch($questStatus)
+ {
+ case 'solved':
+ $questStatusText = $quest['right_text'];
+ break;
+ case 'unsolved':
+ $questStatusText = $quest['wrong_text'];
+ break;
+ }
+ }
+
+ // Media
+ $questmedia = null;
+ if(!is_null($questtext) && array_key_exists('questmedia_id', $questtext) && !empty($questtext['questsmedia_id'])) {
+ $questmedia = $this->Media->getMediaById($questtext['questsmedia_id']);
+ }
+ elseif(!is_null($quest['questsmedia_id'])) {
+ $questmedia = $this->Media->getMediaById($quest['questsmedia_id']);
+ }
+
+ // Task
+ $task = null;
+ if($questtexttypeUrl == 'Prolog')
+ {
+ // Questtype
+ $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']);
+
+ // Render task
+ $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character);
+ }
+
+ // Has Character solved quest?
+ $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+ // Next Quest/Questgroup
+ $nextQuests = null;
+ $nextQuestgroup = null;
+ if($questtexttypeUrl == 'Epilog' || $solved)
+ {
+ if($quest['is_mainquest'])
+ {
+ // Next Quest
+ $nextQuests = $this->Quests->getNextQuests($quest['id']);
+
+ // Next Questgroup
+ if(empty($nextQuests))
+ {
+ $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']);
+ $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyById($nextQuestgroup['questgroupshierarchy_id']);
+ }
+ }
+ else {
+ // Related (Main-) Quest
+ $nextQuest = $relatedQuesttext['quest'];
+ $nextQuest['questgroup_url'] = $questgroup['url'];
+ $nextQuests = array($nextQuest);
+ }
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('questtext', $questtext);
+ $this->set('quest', $quest);
+ $this->set('queststatus', $questStatus);
+ $this->set('queststatustext', $questStatusText);
+ $this->set('relatedquesttext', $relatedQuesttext);
+ $this->set('nextquests', $nextQuests);
+ $this->set('nextquestgroup', $nextQuestgroup);
+ $this->set('task', $task);
+ $this->set('media', $questmedia);
+ $this->set('solved', $solved);
+ }
+
+
+ /**
+ * List Character submissions for a Quest.
+ *
+ * @throws 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);
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Get (related) Questtext (for Sidequests)
+ $relatedQuesttext = null;
+ if(!$quest['is_mainquest'])
+ {
+ $relatedQuesttext = $this->Questtexts->getQuesttextForSidequest($quest['id']);
+ if(!empty($relatedQuesttext)) {
+ $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']);
+ }
+ }
+
+ // Media
+ $questmedia = null;
+ if(!is_null($quest['questsmedia_id'])) {
+ $questmedia = $this->Media->getMediaById($quest['questsmedia_id']);
+ }
+
+ // Get unsolved Character submissions
+ $unsolvedSubmissions = $this->Quests->getCharactersUnsolvedQuest($quest['id']);
+ foreach($unsolvedSubmissions as &$submission) {
+ $submission['character'] = $this->Characters->getCharacterById($submission['character_id']);
+ }
+
+ // Get solved Character submissions
+ $solvedSubmissions = $this->Quests->getCharactersSolvedQuest($quest['id']);
+ foreach($solvedSubmissions as &$submission) {
+ $submission['character'] = $this->Characters->getCharacterById($submission['character_id']);
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('relatedquesttext', $relatedQuesttext);
+ $this->set('media', $questmedia);
+ $this->set('unsolvedsubmissions', $unsolvedSubmissions);
+ $this->set('solvedsubmissions', $solvedSubmissions);
+ }
+
+
+ /**
+ * Show and handle the submission of a Character for a Quest.
+ *
+ * @throws 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);
+
+ // Get Quest
+ $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl);
+
+ // Character
+ $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl);
+
+ // Media
+ $questmedia = null;
+ if(!is_null($quest['questsmedia_id'])) {
+ $questmedia = $this->Media->getMediaById($quest['questsmedia_id']);
+ }
+
+ // Questtype
+ $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']);
+
+ // Render Questtype output
+ $output = $this->renderTaskSubmission($questtype['classname'], $seminary, $questgroup, $quest, $character);
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroup', $questgroup);
+ $this->set('quest', $quest);
+ $this->set('character', $character);
+ $this->set('media', $questmedia);
+ $this->set('output', $output);
+ }
+
+
+
+
+ /**
+ * 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
+ 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']);
+
+ // Redirect
+ $this->redirect($this->linker->link('Epilog', 5, true, array('status'=>'solved')));
+ }
+ elseif($status === false)
+ {
+ // Mark Quest as unsolved
+ $this->Quests->setQuestUnsolved($quest['id'], $character['id']);
+
+ // Redirect
+ $this->redirect($this->linker->link('Prolog', 5, true, array('status'=>'unsolved')));
+ }
+ }
+
+ // 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']);
+ }
+
+ // 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;
+ }
+
+
+ /**
+ * Create a response for the Questtype rendering.
+ *
+ * @param string $action Action to run
+ * @param mixed $param Additional parameters to add to the response
+ * @return 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 Request $request Request
+ * @param Response $response Response
+ * @return QuesttypeAgent
+ */
+ private function loadQuesttypeAgent($questtypeClassname, $request, $response)
+ {
+ // Load Agent
+ \hhu\z\QuesttypeAgent::load($questtypeClassname);
+
+
+ // Construct and return Agent
+ return \hhu\z\QuesttypeAgent::factory($questtypeClassname, $request, $response);
+ }
+
+
+ /**
+ * Run and render the Agent for a QuesttypeAgent and return ist output.
+ *
+ * @param Agent $questtypeAgent QuesttypeAgent to run and render
+ * @param Request $request Request
+ * @param 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();
+ }
+
+ }
+
+?>
diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc
new file mode 100644
index 00000000..86df9068
--- /dev/null
+++ b/controllers/SeminariesController.inc
@@ -0,0 +1,238 @@
+
+ * @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\SeminaryRoleController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('seminaries', 'users', 'questgroupshierarchy', 'questgroups', 'media');
+ /**
+ * 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'),
+ 'delete' => array('admin', 'moderator', 'user')
+ );
+ /**
+ * User seminary permissions
+ *
+ * @var array
+ */
+ public $seminaryPermissions = array(
+ 'seminary' => array('admin', 'moderator', 'user', 'guest'),
+ 'edit' => array('admin', 'moderator'),
+ 'delete' => array('admin', 'moderator')
+ );
+
+
+
+
+ /**
+ * Action: index.
+ *
+ * List registered seminaries.
+ */
+ public function index()
+ {
+ // Get seminaries
+ $seminaries = $this->Seminaries->getSeminaries();
+
+ // Get additional data
+ foreach($seminaries as &$seminary)
+ {
+ // Created user
+ $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']);
+ }
+
+
+ // Pass data to view
+ $this->set('seminaries', $seminaries);
+ }
+
+
+ /**
+ * Action: seminary.
+ *
+ * Show a seminary and its details.
+ *
+ * @throws 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->getHierarchyForSeminary($seminary['id']);
+ foreach($questgroupshierarchy as &$hierarchy)
+ {
+ // Get Questgroups
+ $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']);
+
+ // Get additional data
+ //for($i=0; $i &$questgroup)
+ {
+ // Get first Questgroup text
+ $text = $this->Questgroups->getFirstQuestgroupText($questgroup['id']);
+ if(!empty($text))
+ {
+ $text = \hhu\z\Utils::shortenString($text['text'], 100, 120).' …';
+ $questgroup['text'] = $text;
+ }
+
+ // Get Character XPs
+ $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']);
+
+ // Check permission of Questgroups
+ if($i >= 1) {
+ $questgroup['access'] = $this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']);
+ }
+
+ // Get Media
+ $questgroup['picture'] = null;
+ try {
+ $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']);
+ }
+ catch(\nre\exceptions\IdNotFoundException $e) {
+ }
+ }
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('questgroupshierarchy', $questgroupshierarchy);
+ }
+
+
+ /**
+ * Action: create.
+ *
+ * Create a new seminary.
+ */
+ public function create()
+ {
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Create new seminary
+ var_dump($this->Auth->getUserId());
+ $seminaryId = $this->Seminaries->createSeminary(
+ $this->request->getPostParam('title'),
+ $this->Auth->getUserId()
+ );
+
+ // Redirect to seminary
+ $user = $this->Seminaries->getSeminaryById($seminaryId);
+ $this->redirect($this->linker->link(array($seminary['url']), 1));
+ }
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a seminary.
+ *
+ * @throws IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a seminary
+ */
+ public function edit($seminaryUrl)
+ {
+ // Get seminary
+ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Save changes
+ if(!is_null($this->request->getPostParam('save')))
+ {
+ // Edit seminary
+ $this->Seminaries->editSeminary(
+ $seminary['id'],
+ $this->request->getPostParam('title')
+ );
+ $seminary = $this->Seminaries->getSeminaryById($seminary['id']);
+ }
+
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array($seminary['url']), 1));
+ }
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a seminary.
+ *
+ * @throws 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));
+ }
+
+
+ // Show confirmation
+ $this->set('seminary', $seminary);
+ }
+
+ }
+
+?>
diff --git a/controllers/UserrolesController.inc b/controllers/UserrolesController.inc
new file mode 100644
index 00000000..80c00347
--- /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 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..f6203cc6
--- /dev/null
+++ b/controllers/UsersController.inc
@@ -0,0 +1,238 @@
+
+ * @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\Controller
+ {
+ /**
+ * User permissions
+ *
+ * @var array
+ */
+ public $permissions = array(
+ 'index' => array('admin', 'moderator'),
+ 'user' => array('admin', 'moderator', 'user'),
+ 'create' => array('admin', 'moderator'),
+ 'edit' => array('admin', 'moderator'),
+ 'delete' => array('admin')
+ );
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('users', 'characters');
+
+
+
+
+ /**
+ * Action: index.
+ */
+ public function index()
+ {
+ // Get registered users
+ $users = $this->Users->getUsers();
+
+
+ // Pass data to view
+ $this->set('users', $users);
+ }
+
+
+ /**
+ * Action: user.
+ *
+ * Show a user and its details.
+ *
+ * @throws IdNotFoundException
+ * @param string $userUrl URL-Username of an user
+ */
+ public function user($userUrl)
+ {
+ // Get user
+ $user = $this->Users->getUserByUrl($userUrl);
+
+ // Get Characters
+ $characters = $this->Characters->getCharactersForUser($user['id']);
+
+ // Additional Character information
+ foreach($characters as &$character)
+ {
+ // Level
+ $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']);
+ }
+
+
+ // Pass data to view
+ $this->set('user', $user);
+ $this->set('characters', $characters);
+ }
+
+
+ /**
+ * Action: login.
+ *
+ * Log in a user.
+ */
+ public function login()
+ {
+ $username = '';
+
+ // Log the user in
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('login')))
+ {
+ $username = $this->request->getPostParam('username');
+ $userId = $this->Users->login(
+ $username,
+ $this->request->getPostParam('password')
+ );
+
+ if(!is_null($userId))
+ {
+ $this->Auth->setUserId($userId);
+ $user = $this->Users->getUserById($userId);
+
+ $this->redirect($this->linker->link(array($user['url']), 1));
+ }
+ }
+
+
+ // Pass data to view
+ $this->set('username', $username);
+ $this->set('failed', ($this->request->getRequestMethod() == 'POST'));
+ }
+
+
+ /**
+ * 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: create.
+ *
+ * Create a new user.
+ */
+ public function create()
+ {
+ if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create')))
+ {
+ // Create new user
+ $userId = $this->Users->createUser(
+ $this->request->getPostParam('username'),
+ $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));
+ }
+ }
+
+
+ /**
+ * Action: edit.
+ *
+ * Edit a user.
+ *
+ * @throws IdNotFoundException
+ * @param string $userUrl URL-Username of an user
+ */
+ public function edit($userUrl)
+ {
+ // User
+ $user = $this->Users->getUserByUrl($userUrl);
+
+ // Check request method
+ if($this->request->getRequestMethod() == 'POST')
+ {
+ // Save changes
+ if(!is_null($this->request->getPostParam('save')))
+ {
+ // Edit user
+ $this->Users->editUser(
+ $user['id'],
+ $this->request->getPostParam('username'),
+ $this->request->getPostParam('email'),
+ $this->request->getPostParam('password')
+ );
+ $user = $this->Users->getUserById($user['id']);
+ }
+
+
+ // Redirect to entry
+ $this->redirect($this->linker->link(array($user['url']), 1));
+ }
+
+
+ // Pass data to view
+ $this->set('user', $user);
+ }
+
+
+ /**
+ * Action: delete.
+ *
+ * Delete a user.
+ *
+ * @throws 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));
+ }
+
+
+ // Show confirmation
+ $this->set('user', $user);
+ }
+
+
+ }
+
+?>
diff --git a/controllers/components/AchievementComponent.inc b/controllers/components/AchievementComponent.inc
new file mode 100644
index 00000000..70601543
--- /dev/null
+++ b/controllers/components/AchievementComponent.inc
@@ -0,0 +1,41 @@
+
+ * @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');
+
+
+
+
+ /**
+ * Construct a new Achievements-component.
+ */
+ public function __construct()
+ {
+ }
+
+ }
+
+?>
diff --git a/controllers/components/AuthComponent.inc b/controllers/components/AuthComponent.inc
new file mode 100644
index 00000000..0db6c69d
--- /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/core/Agent.inc b/core/Agent.inc
new file mode 100644
index 00000000..00ec1a90
--- /dev/null
+++ b/core/Agent.inc
@@ -0,0 +1,607 @@
+
+ * @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 AgentNotFoundException
+ * @throws 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 DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ControllerNotValidException
+ * @throws 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 DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ControllerNotValidException
+ * @throws 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 ParamsNotValidException
+ * @throws IdNotFoundException
+ * @throws DatamodelException
+ * @throws 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 DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ControllerNotValidException
+ * @throws 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 ServiceUnavailableException
+ * @throws AgentNotFoundException
+ * @throws 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 ServiceUnavailableException
+ * @throws DatamodelException
+ * @throws AgentNotFoundException
+ * @throws 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 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 string $label Name of the original Agent
+ * @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..b89b12e7
--- /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 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 $respone 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 DatamodelException
+ * @throws DriverNotValidException
+ * @throws DriverNotFoundException
+ * @throws ViewNotFoundException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ControllerNotValidException
+ * @throws ControllerNotFoundException
+ * @throws AgentNotValidException
+ * @throws 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 ServiceUnavailableException
+ * @throws AgentNotValidException
+ * @throws 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..020b61f7
--- /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 Die zu ladende Klasse
+ */
+ 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..81cf537a
--- /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 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 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 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 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 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..a93363dc
--- /dev/null
+++ b/core/Component.inc
@@ -0,0 +1,85 @@
+
+ * @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 ComponentNotFoundException
+ * @throws 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";
+ }
+
+ }
+
+?>
diff --git a/core/Config.inc b/core/Config.inc
new file mode 100644
index 00000000..b51f1e47
--- /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..c2814e99
--- /dev/null
+++ b/core/Controller.inc
@@ -0,0 +1,424 @@
+
+ * @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 ControllerNotFoundException
+ * @throws 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 DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws ViewNotFoundException
+ * @param string $controllerName Name of the Controller to instantiate
+ * @param string $layoutName Name of the current Layout
+ * @param string $action Current Action
+ */
+ 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 DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ * @throws 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;
+
+
+ // Linker erstellen
+ $this->set('linker', new \nre\core\Linker($request));
+
+ }
+
+
+ /**
+ * 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 ParamsNotValidException
+ * @throws IdNotFoundException
+ * @throws DatamodelException
+ * @throws 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 ComponentNotValidException
+ * @throws ComponentNotFoundException
+ */
+ private function loadComponents()
+ {
+ // Determine components
+ $components = array();
+ if(property_exists($this, 'components')) {
+ $components = $this->components;
+ }
+ if(!is_array($components)) {
+ $components = array($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 DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws 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']);
+ }
+ }
+
+ // 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 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..eec59143
--- /dev/null
+++ b/core/Driver.inc
@@ -0,0 +1,96 @@
+
+ * @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 DriverNotFoundException
+ * @throws 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
+ */
+ 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..a17a700f
--- /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..00c5846b
--- /dev/null
+++ b/core/Linker.inc
@@ -0,0 +1,311 @@
+
+ * @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)
+ {
+ // Mask special signs seperately
+ $specials = array('/', '?', '&');
+ foreach($specials as &$special) {
+ $param = str_replace($special, rawurlencode(rawurlencode($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[] = rawurlencode($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_map('rawurlencode', $params);
+ $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)
+ )
+ );
+ }
+
+ // 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 bool $absolute Create absolute link
+ * @param string $anchor Anchor on target
+ * @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..b5ac7dc9
--- /dev/null
+++ b/core/Logger.inc
@@ -0,0 +1,132 @@
+
+ * @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_SCREEN)
+ {
+ // 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 ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP;
+ }
+
+ }
+
+?>
diff --git a/core/Model.inc b/core/Model.inc
new file mode 100644
index 00000000..c0475b4a
--- /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 ModelNotFoundException
+ * @throws 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 DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws ModelNotFoundException
+ */
+ protected function __construct()
+ {
+ // Load Models
+ $this->loadModels();
+ }
+
+
+
+
+ /**
+ * Load the Models of this Model.
+ *
+ * @throws DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @throws ModelNotValidException
+ * @throws 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..5fda187e
--- /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..4781b2ab
--- /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..546ddb6e
--- /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 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 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) != $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..5a4bb6f0
--- /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/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc
new file mode 100644
index 00000000..dfd5c0fd
--- /dev/null
+++ b/drivers/DatabaseDriver.inc
@@ -0,0 +1,87 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\drivers;
+
+
+ /**
+ * Abstract class for implementing a database driver.
+ *
+ * @author coderkun
+ */
+ abstract class DatabaseDriver extends \nre\core\Driver
+ {
+ /**
+ * Driver class instance
+ *
+ * @static
+ * @var DatabaseDriver
+ */
+ protected static $driver = null;
+ /**
+ * Connection resource
+ *
+ * @var resource
+ */
+ protected $connection = null;
+
+
+
+
+ /**
+ * Singleton-pattern.
+ *
+ * @param array $config Database driver configuration
+ * @return DatabaseDriver Singleton-instance of database driver class
+ */
+ public static function singleton($config)
+ {
+ // Singleton
+ if(self::$driver !== null) {
+ return self::$driver;
+ }
+
+ // Construct
+ $className = get_called_class();
+ self::$driver = new $className($config);
+
+
+ return self::$driver;
+ }
+
+
+ /**
+ * Construct a new database driver.
+ *
+ * @param array $config Connection and login settings
+ */
+ protected function __construct($config)
+ {
+ parent::__construct();
+
+ // Establish connection
+ $this->connect($config);
+ }
+
+
+
+
+ /**
+ * Establish a connect to a MqSQL-database.
+ *
+ * @throws DatamodelException
+ * @param array $config Connection and login settings
+ */
+ protected abstract function connect($config);
+
+ }
+
+?>
diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc
new file mode 100644
index 00000000..fe4fa81a
--- /dev/null
+++ b/drivers/MysqliDriver.inc
@@ -0,0 +1,169 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\drivers;
+
+
+ /**
+ * Implementation of a database driver for MySQL-databases.
+ *
+ * @author coderkun
+ */
+ class MysqliDriver extends \nre\drivers\DatabaseDriver
+ {
+
+
+
+
+ /**
+ * Construct a MySQL-driver.
+ *
+ * @throws DatamodelException
+ * @param array $config Connection and login settings
+ */
+ function __construct($config)
+ {
+ parent::__construct($config);
+ }
+
+
+
+
+ /**
+ * Execute a SQL-query.
+ *
+ * @throws DatamodelException
+ * @param string $query Query to run
+ * @param mixed … Params
+ * @return array Result
+ */
+ public function query($query)
+ {
+ // Prepare statement
+ if(!($stmt = $this->connection->prepare($query))) {
+ throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno);
+ }
+
+ try {
+ // Prepare data
+ $data = array();
+
+ // Bind parameters
+ $p = array_slice(func_get_args(), 1);
+ $params = array();
+ foreach($p as $key => $value) {
+ $params[$key] = &$p[$key];
+ }
+ if(count($params) > 0)
+ {
+ if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) {
+ throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno);
+ }
+ }
+
+ // Execute query
+ if(!$stmt->execute()) {
+ throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno);
+ }
+
+ // Fetch result
+ if($result = $stmt->get_result()) {
+ while($row = $result->fetch_assoc()) {
+ $data[] = $row;
+ }
+ }
+
+
+ $stmt->close();
+ return $data;
+ }
+ catch(Exception $e) {
+ $stmt->close();
+ throw $e;
+ }
+ }
+
+
+ /**
+ * Return the last insert id (of the last insert-query).
+ *
+ * @return int Last insert id
+ */
+ public function getInsertId()
+ {
+ return $this->connection->insert_id;
+ }
+
+
+ /**
+ * Enable/disable autocommit feature.
+ *
+ * @param boolean $autocommit Enable/disable autocommit
+ */
+ public function setAutocommit($autocommit)
+ {
+ $this->connection->autocommit($autocommit);
+ }
+
+
+ /**
+ * Rollback the current transaction.
+ */
+ public function rollback()
+ {
+ $this->connection->rollback();
+ }
+
+
+ /**
+ * Commit the current transaction.
+ */
+ public function commit()
+ {
+ $this->connection->commit();
+ }
+
+
+
+
+ /**
+ * Establish a connect to a MqSQL-database.
+ *
+ * @throws DatamodelException
+ * @param array $config Connection and login settings
+ */
+ protected function connect($config)
+ {
+ // Connect
+ $con = @new \mysqli(
+ $config['host'],
+ $config['user'],
+ $config['password'],
+ $config['db']
+ );
+
+ // Check connection
+ if($con->connect_error) {
+ throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno);
+ }
+
+ // Set character encoding
+ if(!$con->set_charset('utf8')) {
+ throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno);
+ }
+
+ // Save connection
+ $this->connection = $con;
+ }
+
+ }
+
+?>
diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc
new file mode 100644
index 00000000..31bba929
--- /dev/null
+++ b/exceptions/AccessDeniedException.inc
@@ -0,0 +1,51 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Access denied.
+ *
+ * @author coderkun
+ */
+ class AccessDeniedException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 85;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'access denied';
+
+
+
+
+ /**
+ * Consturct a new exception.
+ */
+ function __construct()
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+ }
+
+?>
diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc
new file mode 100644
index 00000000..418dd49c
--- /dev/null
+++ b/exceptions/ActionNotFoundException.inc
@@ -0,0 +1,77 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Action not found.
+ *
+ * @author coderkun
+ */
+ class ActionNotFoundException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 70;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'action not found';
+
+ /**
+ * Name of the action that was not found
+ *
+ * @var string
+ */
+ private $action;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $action Name of the action that was not found
+ */
+ function __construct($action)
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE,
+ $action
+ );
+
+ // Store values
+ $this->action = $action;
+ }
+
+
+
+
+ /**
+ * Get the name of the action that was not found.
+ *
+ * @return string Name of the action that was not found
+ */
+ public function getAction()
+ {
+ return $this->action;
+ }
+
+ }
+
+?>
diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc
new file mode 100644
index 00000000..f0b3a2a7
--- /dev/null
+++ b/exceptions/AgentNotFoundException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Agent not found.
+ *
+ * @author coderkun
+ */
+ class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 66;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'agent not found';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $agentName Name of the Agent that was not found
+ */
+ function __construct($agentName)
+ {
+ parent::__construct(
+ $agentName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the Agent that was not found.
+ *
+ * @return string Name of the Agent that was not found
+ */
+ public function getAgentName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc
new file mode 100644
index 00000000..1c752051
--- /dev/null
+++ b/exceptions/AgentNotValidException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Agent not valid.
+ *
+ * @author coderkun
+ */
+ class AgentNotValidException extends \nre\exceptions\ClassNotValidException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 76;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'agent not valid';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $agentName Name of the invalid Agent
+ */
+ function __construct($agentName)
+ {
+ parent::__construct(
+ $agentName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid Agent.
+ *
+ * @return string Name of the invalid Agent
+ */
+ public function getAgentName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc
new file mode 100644
index 00000000..070f383f
--- /dev/null
+++ b/exceptions/ClassNotFoundException.inc
@@ -0,0 +1,77 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Class not found.
+ *
+ * @author coderkun
+ */
+ class ClassNotFoundException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 64;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'class not found';
+
+ /**
+ * Name of the class that was not found
+ *
+ * @var string
+ */
+ private $className;
+
+
+
+
+ /**
+ * Construct a new exception
+ *
+ * @param string $className Name of the class that was not found
+ */
+ function __construct($className, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $className
+ );
+
+ // Store values
+ $this->className = $className;
+ }
+
+
+
+
+ /**
+ * Get the name of the class that was not found.
+ *
+ * @return string Name of the class that was not found
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ }
+
+?>
diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc
new file mode 100644
index 00000000..bdd36d5e
--- /dev/null
+++ b/exceptions/ClassNotValidException.inc
@@ -0,0 +1,77 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Class not valid.
+ *
+ * @author coderkun
+ */
+ class ClassNotValidException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 74;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'class not valid';
+
+ /**
+ * Name of the invalid class
+ *
+ * @var string
+ */
+ private $className;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $className Name of the invalid class
+ */
+ function __construct($className, $message=self::MESSAGE, $code=self::CODE)
+ {
+ parent::__construct(
+ $message,
+ $code,
+ $className
+ );
+
+ // Store value
+ $this->className = $className;
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid class.
+ *
+ * @return string Name of the invalid class
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ }
+
+?>
diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc
new file mode 100644
index 00000000..5e75de44
--- /dev/null
+++ b/exceptions/ComponentNotFoundException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Component not found.
+ *
+ * @author coderkun
+ */
+ class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 67;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'component not found';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $componentName Name of the Component that was not found
+ */
+ function __construct($componentName)
+ {
+ parent::__construct(
+ $componentName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the Component that was not found.
+ *
+ * @return string Name of the Component that was not found
+ */
+ public function getComponentName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc
new file mode 100644
index 00000000..a03b0c0d
--- /dev/null
+++ b/exceptions/ComponentNotValidException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Component not valid.
+ *
+ * @author coderkun
+ */
+ class ComponentNotValidException extends \nre\exceptions\ClassNotValidException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 77;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'component not valid';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $componentName Name of the invalid Component
+ */
+ function __construct($componentName)
+ {
+ parent::__construct(
+ $componentName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid Component.
+ *
+ * @return string Name of the invalid Component
+ */
+ public function getComponentName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc
new file mode 100644
index 00000000..90859c49
--- /dev/null
+++ b/exceptions/ControllerNotFoundException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Controller not found.
+ *
+ * @author coderkun
+ */
+ class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 67;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'controller not found';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $controllerName Name of the Controller that was not found
+ */
+ function __construct($controllerName)
+ {
+ parent::__construct(
+ $controllerName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the Controller that was not found.
+ *
+ * @return string Name of the Controller that was not found
+ */
+ public function getControllerName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc
new file mode 100644
index 00000000..0c945bcb
--- /dev/null
+++ b/exceptions/ControllerNotValidException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Controller not valid.
+ *
+ * @author coderkun
+ */
+ class ControllerNotValidException extends \nre\exceptions\ClassNotValidException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 77;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'controller not valid';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $controllerName Name of the invalid Controller
+ */
+ function __construct($controllerName)
+ {
+ parent::__construct(
+ $controllerName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid Controller.
+ *
+ * @return string Name of the invalid Controller
+ */
+ public function getControllerName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc
new file mode 100644
index 00000000..7785cd21
--- /dev/null
+++ b/exceptions/DatamodelException.inc
@@ -0,0 +1,99 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Datamodel.
+ *
+ * This exception is thrown when an error occurred during the execution
+ * of a datamodel.
+ *
+ * @author coderkun
+ */
+ class DatamodelException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 84;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'datamodel error';
+
+ /**
+ * Error message of datamodel
+ *
+ * @var string
+ */
+ private $datamodelMessage;
+ /**
+ * Error code of datamodel
+ *
+ * @var int
+ */
+ private $datamodelErrorNumber;
+
+
+
+
+ /**
+ * Consturct a new exception.
+ *
+ * @param string $datamodelMessage Error message of datamodel
+ * @param int $datamodelErrorNumber Error code of datamodel
+ */
+ function __construct($datamodelMessage, $datamodelErrorNumber)
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE,
+ $datamodelMessage." ($datamodelErrorNumber)"
+ );
+
+ // Store values
+ $this->datamodelMessage = $datamodelMessage;
+ $this->datamodelErrorNumber = $datamodelErrorNumber;
+ }
+
+
+
+
+ /**
+ * Get the error message of datamodel.
+ *
+ * @return string Error message of datamodel
+ */
+ public function getDatamodelMessage()
+ {
+ return $this->datamodelMessage;
+ }
+
+
+ /**
+ * Get the error code of datamodel.
+ *
+ * @return string Error code of datamodel
+ */
+ public function getDatamodelErrorNumber()
+ {
+ return $this->datamodelErrorNumber;
+ }
+
+ }
+
+?>
diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc
new file mode 100644
index 00000000..9b218f29
--- /dev/null
+++ b/exceptions/DriverNotFoundException.inc
@@ -0,0 +1,68 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Driver not found.
+ *
+ * @author coderkun
+ */
+ class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 71;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'driver not found';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $driverName Name of the driver that was not found
+ */
+ function __construct($driverName)
+ {
+ // Elternkonstruktor aufrufen
+ parent::__construct(
+ $driverName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the driver that was not found.
+ *
+ * @return string Name of the driver that was not found
+ */
+ public function getDriverName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc
new file mode 100644
index 00000000..fa9022e8
--- /dev/null
+++ b/exceptions/DriverNotValidException.inc
@@ -0,0 +1,68 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Driver not valid.
+ *
+ * @author coderkun
+ */
+ class DriverNotValidException extends \nre\exceptions\ClassNotValidException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 81;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'driver not valid';
+
+
+
+
+ /**
+ * Konstruktor.
+ *
+ * @param string $driverName Name of the invalid driver
+ */
+ function __construct($driverName)
+ {
+ // Elternkonstruktor aufrufen
+ parent::__construct(
+ $driverName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid driver.
+ *
+ * @return string Name of the invalid driver
+ */
+ public function getDriverName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc
new file mode 100644
index 00000000..afd80b86
--- /dev/null
+++ b/exceptions/FatalDatamodelException.inc
@@ -0,0 +1,96 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Datamodel exception that is fatal for the application.
+ *
+ * @author coderkun
+ */
+ class FatalDatamodelException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 85;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'fatal datamodel error';
+
+ /**
+ * Error message of datamodel
+ *
+ * @var string
+ */
+ private $datamodelMessage;
+ /**
+ * Error code of datamodel
+ *
+ * @var int
+ */
+ private $datamodelErrorNumber;
+
+
+
+
+ /**
+ * Consturct a new exception.
+ *
+ * @param string $datamodelMessage Error message of datamodel
+ * @param int $datamodelErrorNumber Error code of datamodel
+ */
+ function __construct($datamodelMessage, $datamodelErrorNumber)
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE,
+ $datamodelMessage." ($datamodelErrorNumber)"
+ );
+
+ // Store values
+ $this->datamodelMessage = $datamodelMessage;
+ $this->datamodelErrorNumber = $datamodelErrorNumber;
+ }
+
+
+
+
+ /**
+ * Get the error message of datamodel.
+ *
+ * @return string Error message of datamodel
+ */
+ public function getDatamodelMessage()
+ {
+ return $this->datamodelMessage;
+ }
+
+
+ /**
+ * Get the error code of datamodel.
+ *
+ * @return string Error code of datamodel
+ */
+ public function getDatamodelErrorNumber()
+ {
+ return $this->datamodelErrorNumber;
+ }
+
+ }
+
+?>
diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc
new file mode 100644
index 00000000..fc3315c3
--- /dev/null
+++ b/exceptions/IdNotFoundException.inc
@@ -0,0 +1,77 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: ID not found.
+ *
+ * @author coderkun
+ */
+ class IdNotFoundException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 85;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'id not found';
+
+ /**
+ * ID that was not found
+ *
+ * @var mixed
+ */
+ private $id;
+
+
+
+
+ /**
+ * Consturct a new exception.
+ *
+ * @param mixed $id ID that was not found
+ */
+ function __construct($id)
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE,
+ $id
+ );
+
+ // Store values
+ $this->id = $id;
+ }
+
+
+
+
+ /**
+ * Get the ID that was not found.
+ *
+ * @return mixed ID that was not found
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ }
+
+?>
diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc
new file mode 100644
index 00000000..0eb8c89c
--- /dev/null
+++ b/exceptions/LayoutNotFoundException.inc
@@ -0,0 +1,68 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Layout not found.
+ *
+ * @author coderkun
+ */
+ class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 65;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'layout not found';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $layoutName Name of the Layout that was not found
+ */
+ function __construct($layoutName)
+ {
+ // Elternkonstruktor aufrufen
+ parent::__construct(
+ $layoutName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the Layout that was not found.
+ *
+ * @return string Name of the Layout that was not found
+ */
+ public function getLayoutName()
+ {
+ return $this->getAgentName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc
new file mode 100644
index 00000000..b3af184f
--- /dev/null
+++ b/exceptions/LayoutNotValidException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Layout not valid.
+ *
+ * @author coderkun
+ */
+ class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 75;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'layout not valid';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $layoutName Name of the invalid Layout
+ */
+ function __construct($layoutName)
+ {
+ parent::__construct(
+ $layoutName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid Layout.
+ *
+ * @return string Name of the invalid Layout
+ */
+ public function getLayoutName()
+ {
+ return $this->getAgentName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc
new file mode 100644
index 00000000..48e8c0df
--- /dev/null
+++ b/exceptions/ModelNotFoundException.inc
@@ -0,0 +1,67 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Action not found.
+ *
+ * @author coderkun
+ */
+ class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 68;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'model not found';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $modelName Name of the Model that was not found
+ */
+ function __construct($modelName)
+ {
+ parent::__construct(
+ $modelName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the Model that was not found
+ *
+ * @return string Name of the Model that was not found
+ */
+ public function getModelName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc
new file mode 100644
index 00000000..267e460e
--- /dev/null
+++ b/exceptions/ModelNotValidException.inc
@@ -0,0 +1,68 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Action not found.
+ *
+ * @author coderkun
+ */
+ class ModelNotValidException extends \nre\exceptions\ClassNotValidException
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 78;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'model not valid';
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $modelName Name of the invalid Model
+ */
+ function __construct($modelName)
+ {
+ // Elternkonstruktor aufrufen
+ parent::__construct(
+ $modelName,
+ self::MESSAGE,
+ self::CODE
+ );
+ }
+
+
+
+
+ /**
+ * Get the name of the invalid Model
+ *
+ * @return string Name of the invalid Model
+ */
+ public function getModelName()
+ {
+ return $this->getClassName();
+ }
+
+ }
+
+?>
diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc
new file mode 100644
index 00000000..be650ce2
--- /dev/null
+++ b/exceptions/ParamsNotValidException.inc
@@ -0,0 +1,77 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception Parameters not valid.
+ *
+ * @author coderkun
+ */
+ class ParamsNotValidException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 86;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'parameters not valid';
+
+ /**
+ * Invalid parameters.
+ *
+ * @var array
+ */
+ private $params;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param mixed $param1 Invalid parameters as argument list
+ */
+ function __construct($param1)
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE,
+ implode(', ', func_get_args())
+ );
+
+ // Store values
+ $this->params = func_get_args();
+ }
+
+
+
+
+ /**
+ * Get invalid parameters.
+ *
+ * @return array Invalid parameters
+ */
+ public function getParams()
+ {
+ return $this->params;
+ }
+
+ }
+
+?>
diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc
new file mode 100644
index 00000000..bafc297f
--- /dev/null
+++ b/exceptions/ServiceUnavailableException.inc
@@ -0,0 +1,77 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: Service is unavailable.
+ *
+ * @author coderkun
+ */
+ class ServiceUnavailableException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 84;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'service unavailable';
+
+ /**
+ * Throws exception
+ *
+ * @var Exception
+ */
+ private $exception;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param Exception $exception Exception that has occurred
+ */
+ function __construct($exception)
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE,
+ $exception->getMessage()
+ );
+
+ // Store values
+ $this->exception = $exception;
+ }
+
+
+
+
+ /**
+ * Get the exception that hat occurred
+ *
+ * @return Exception Exception that has occurred
+ */
+ public function getException()
+ {
+ return $this->exception;
+ }
+
+ }
+
+?>
diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc
new file mode 100644
index 00000000..30366201
--- /dev/null
+++ b/exceptions/ViewNotFoundException.inc
@@ -0,0 +1,77 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\exceptions;
+
+
+ /**
+ * Exception: View not found.
+ *
+ * @author coderkun
+ */
+ class ViewNotFoundException extends \nre\core\Exception
+ {
+ /**
+ * Error code
+ *
+ * @var int
+ */
+ const CODE = 69;
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ const MESSAGE = 'view not found';
+
+ /**
+ * Filename of the view that was not found
+ *
+ * @var string
+ */
+ private $fileName;
+
+
+
+
+ /**
+ * Construct a new exception.
+ *
+ * @param string $fileName Filename of the view that was not found
+ */
+ function __construct($fileName)
+ {
+ parent::__construct(
+ self::MESSAGE,
+ self::CODE,
+ $fileName
+ );
+
+ // Save values
+ $this->fileName = $fileName;
+ }
+
+
+
+
+ /**
+ * Get the filename of the view that was not found.
+ *
+ * @return string Filename of the view that was not found
+ */
+ public function getFileName()
+ {
+ return $this->fileName;
+ }
+
+ }
+
+?>
diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo
new file mode 100644
index 0000000000000000000000000000000000000000..1192a6534877b0811d705ef9721053ec44e8748f
GIT binary patch
literal 2782
zcmaKsO>7)B6vqt>P_{rnfQF9(E+vrAPO_U8s5T0vNw#TeQqp8wz6G;0Z)c~Oc(%4D
zOL{=%1RQ(dfYg>7s5eeULR_E+B!rNVIB+Ub)Ejrig^K^1aduZ~!5Yuc&wjSwhwbY-
z20jxgWz=1$7w-^an%kT3Lm3_rVjDOD4uVy%1TKOPg71P`!LyDZfm;CBE-@*GJUw8b|_5ba76TBPa1DMSFZvk0
z<#-h2`6s|J@J*0?{}g0@z6IH?UqEar{s12VZ-Ds3Kdyfh7Uz98gWSIzWd8<1d}6ob
zFv#nUIeFUgRgm{N3GzM-*B^nrt^&V6E;=CV4Wg6dKMb<{uYeDM$3eF5eURh425tvG
z201>TgPfl)LAK*6h$Y2OAg}wY>t6?X{$C*HZvf7)U)vmaf;?{*$m{I^*^d3-1K`Ub
zriyux=bZ*w{~gD`@r)Zk3sx}R0juB@@KNvv$adWX?*X@Bljp!ekk>r~@;)_?{X600
zw?Vcm1Tj@u#}C1WA%6n$yvvSXfgGo=LDv5PENG%v(i%IVWr<$7BdqqH^Jw
zu)iCHedIEX%6aC(er=RdG}!+5wNgGPbWDO-;Tfsi-3jt;EXLYyEwhAuVPoA?S3+o$SU;pyRfUQzdF)Ybktxv6rF{17`GQ&y*87Uav#(JCckE{$Tv|1`6J
zv4s=jM8Cx>-$cVacmh+!9
zSr8yF&5SoTX|hV8sK+)Taf;KxKjp6!k`b?Cg~H3gR{R#_ieiIfLZKTr{wfK#VWwPTL8oV6w|6Y`PLpPXtFn$z%~3=OGn?|Q(!y(XFF!#T
z^=5F8s&c$sIpCG|d*kDByi%>aw6|O-m!aq_(rV1B++Ocsp*QCn>(w=4mH0N*Rau(z
zRh0P=d3Ewv`s6C|^4?6XG=FTq)-$j&S}ske%96q|Yi-zL=>t2~PW)I+$N(3_kexhL
zKjIzi>G5_=(q2skX&9@hDi1bdTYAHrB}KZhV<9Cz-IdW7M-O165WOkiP}{eysvIpX
zB$@UT??|dwOjW9OE}F^l30X8Jhn3ttDJvrrz4=RNrUT-83$v=Su&cZJ68So4Ent1G
zj2%YDif=WhjwuzFdl_krbk=T@>fLtN;q~8kRwP`SjFdc{X(N$ROG$!xMduW$%$~!k
z?hWL6$+fE{XhE&3(AR?zO>rYrk;s)Bu(&}n!rxQx>pMcN1P8hACE$mE#L*c0M%1#B
zjO_4yjdvmUDk-E|teV%pYiZu;HtzxsXp(AVtNS=)SdmT2z0-2Tc;Oxhd&>jCkyymh
zPE_s+iKR42aK?&daMv&MyKqrGsZ2CsnE!K)=zzqs3|O9S7I4&JgLZ@YB*g4H20(X>Hx}qLEWPdbth;U=dy~
OQ3TBjxR{5}ihlv^jGSfw
literal 0
HcmV?d00001
diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po
new file mode 100644
index 00000000..56d1ec82
--- /dev/null
+++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po
@@ -0,0 +1,270 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: The Legend of Z\n"
+"POT-Creation-Date: 2014-03-22 21:17+0100\n"
+"PO-Revision-Date: 2014-03-22 21:18+0100\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: de_DE\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.6.4\n"
+"X-Poedit-Basepath: .\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: ../../../views\n"
+
+#: ../../../views/binary/error/index.tpl:1
+#: ../../../views/html/error/index.tpl:1
+msgid "Error"
+msgstr "Fehler"
+
+#: ../../../views/html/charactergroups/group.tpl:3
+#: ../../../views/html/charactergroups/groupsgroup.tpl:3
+#: ../../../views/html/charactergroups/index.tpl:3
+#: ../../../views/html/characters/character.tpl:19
+#: ../../../views/html/seminaries/seminary.tpl:42
+msgid "Character Groups"
+msgstr "Charaktergruppen"
+
+#: ../../../views/html/charactergroups/group.tpl:12
+#: ../../../views/html/characters/character.tpl:2
+#: ../../../views/html/characters/index.tpl:2
+#: ../../../views/html/seminaries/seminary.tpl:41
+#: ../../../views/html/users/user.tpl:11
+msgid "Characters"
+msgstr "Charaktere"
+
+#: ../../../views/html/charactergroups/group.tpl:15
+msgid "Group Leader"
+msgstr "Gruppenleiter"
+
+#: ../../../views/html/charactergroups/group.tpl:21
+#: ../../../views/html/questgroups/questgroup.tpl:35
+msgid "Quests"
+msgstr "Quests"
+
+#: ../../../views/html/charactergroups/groupsgroup.tpl:13
+#: ../../../views/html/charactergroupsquests/quest.tpl:3
+msgid "Character Groups Quests"
+msgstr "Charactergruppen-Quests"
+
+#: ../../../views/html/charactergroupsquests/quest.tpl:12
+#: ../../../views/html/seminaries/seminary.tpl:2
+msgid "Description"
+msgstr "Beschreibung"
+
+#: ../../../views/html/charactergroupsquests/quest.tpl:15
+msgid "Rules"
+msgstr "Regeln"
+
+#: ../../../views/html/charactergroupsquests/quest.tpl:22
+msgid "Won Quest"
+msgstr "Gewonnene Quest"
+
+#: ../../../views/html/charactergroupsquests/quest.tpl:28
+msgid "Lost Quest"
+msgstr "Verlorene Quest"
+
+#: ../../../views/html/characters/character.tpl:7
+#: ../../../views/html/characters/index.tpl:6
+#: ../../../views/html/users/user.tpl:14
+msgid "Level"
+msgstr "Level"
+
+#: ../../../views/html/characters/character.tpl:8
+msgid "User"
+msgstr "Benutzer"
+
+#: ../../../views/html/introduction/index.tpl:1
+#: ../../../views/html/menu/index.tpl:2
+msgid "Introduction"
+msgstr "Einführung"
+
+#: ../../../views/html/menu/index.tpl:3 ../../../views/html/users/create.tpl:1
+#: ../../../views/html/users/delete.tpl:1 ../../../views/html/users/edit.tpl:1
+#: ../../../views/html/users/index.tpl:1 ../../../views/html/users/login.tpl:1
+#: ../../../views/html/users/user.tpl:1
+msgid "Users"
+msgstr "Benutzer"
+
+#: ../../../views/html/menu/index.tpl:4
+#: ../../../views/html/seminaries/create.tpl:1
+#: ../../../views/html/seminaries/delete.tpl:1
+#: ../../../views/html/seminaries/edit.tpl:1
+#: ../../../views/html/seminaries/index.tpl:1
+msgid "Seminaries"
+msgstr "Kurse"
+
+#: ../../../views/html/menu/index.tpl:10 ../../../views/html/users/login.tpl:2
+#: ../../../views/html/users/login.tpl:11
+msgid "Login"
+msgstr "Login"
+
+#: ../../../views/html/menu/index.tpl:12
+msgid "Logout"
+msgstr "Logout"
+
+#: ../../../views/html/questgroups/questgroup.tpl:26
+#: ../../../views/html/questgroups/questgroup.tpl:51
+#: ../../../views/html/seminaries/seminary.tpl:28
+msgid "locked"
+msgstr "gesperrt"
+
+#: ../../../views/html/quests/quest.tpl:20
+#: ../../../views/html/quests/submissions.tpl:27
+msgid "solved"
+msgstr "gelöst"
+
+#: ../../../views/html/quests/quest.tpl:22
+#: ../../../views/html/quests/submissions.tpl:18
+msgid "unsolved"
+msgstr "ungelöst"
+
+#: ../../../views/html/quests/quest.tpl:59
+msgid "Task"
+msgstr "Aufgabe"
+
+#: ../../../views/html/quests/quest.tpl:67
+msgid "Task successfully solved"
+msgstr "Du hast die Aufgabe erfolgreich gelöst"
+
+#: ../../../views/html/quests/quest.tpl:67
+msgid "Show correct answer"
+msgstr "Richtige Lösung anzeigen"
+
+#: ../../../views/html/quests/quest.tpl:68
+msgid "Go on"
+msgstr "Hier geht es weiter"
+
+#: ../../../views/html/quests/quest.tpl:73
+#: ../../../views/html/quests/quest.tpl:75
+msgid "Quest"
+msgstr "Quest"
+
+#: ../../../views/html/quests/submission.tpl:10
+#, php-format
+msgid "Submission of %s"
+msgstr "Lösungen von %s"
+
+#: ../../../views/html/seminaries/create.tpl:2
+msgid "New seminary"
+msgstr "Neuer Kurs"
+
+#: ../../../views/html/seminaries/create.tpl:6
+#: ../../../views/html/seminaries/create.tpl:7
+#: ../../../views/html/seminaries/edit.tpl:6
+#: ../../../views/html/seminaries/edit.tpl:7
+msgid "Title"
+msgstr "Titel"
+
+#: ../../../views/html/seminaries/create.tpl:9
+#: ../../../views/html/users/create.tpl:13
+msgid "create"
+msgstr "erstellen"
+
+#: ../../../views/html/seminaries/delete.tpl:2
+#: ../../../views/html/seminaries/seminary.tpl:38
+msgid "Delete seminary"
+msgstr "Kurs löschen"
+
+#: ../../../views/html/seminaries/delete.tpl:4
+#, php-format
+msgid "Should the seminary “%s” really be deleted?"
+msgstr "Soll der Kurs „%s“ wirklich gelöscht werden?"
+
+#: ../../../views/html/seminaries/delete.tpl:6
+#: ../../../views/html/users/delete.tpl:6
+msgid "delete"
+msgstr "löschen"
+
+#: ../../../views/html/seminaries/delete.tpl:7
+#: ../../../views/html/users/delete.tpl:7
+msgid "cancel"
+msgstr "abbrechen"
+
+#: ../../../views/html/seminaries/edit.tpl:2
+#: ../../../views/html/seminaries/seminary.tpl:37
+msgid "Edit seminary"
+msgstr "Kurs bearbeiten"
+
+#: ../../../views/html/seminaries/edit.tpl:9
+#: ../../../views/html/users/edit.tpl:13
+msgid "save"
+msgstr "speichern"
+
+#: ../../../views/html/seminaries/index.tpl:3
+msgid "Create new seminary"
+msgstr "Neuen Kurs erstellen"
+
+#: ../../../views/html/seminaries/index.tpl:10
+#: ../../../views/html/seminaries/seminary.tpl:45
+#, php-format
+msgid "created by %s on %s"
+msgstr "erstellt von %s am %s"
+
+#: ../../../views/html/users/create.tpl:2
+msgid "New user"
+msgstr "Neuer Benutzer"
+
+#: ../../../views/html/users/create.tpl:6
+#: ../../../views/html/users/create.tpl:7 ../../../views/html/users/edit.tpl:6
+#: ../../../views/html/users/edit.tpl:7 ../../../views/html/users/login.tpl:6
+#: ../../../views/html/users/login.tpl:7
+msgid "Username"
+msgstr "Benutzername"
+
+#: ../../../views/html/users/create.tpl:8
+#: ../../../views/html/users/create.tpl:9 ../../../views/html/users/edit.tpl:8
+#: ../../../views/html/users/edit.tpl:9
+msgid "E‑Mail-Address"
+msgstr "E‑Mail-Adresse"
+
+#: ../../../views/html/users/create.tpl:10
+#: ../../../views/html/users/create.tpl:11
+#: ../../../views/html/users/edit.tpl:10 ../../../views/html/users/edit.tpl:11
+#: ../../../views/html/users/login.tpl:8 ../../../views/html/users/login.tpl:9
+msgid "Password"
+msgstr "Passwort"
+
+#: ../../../views/html/users/delete.tpl:2 ../../../views/html/users/user.tpl:5
+msgid "Delete user"
+msgstr "Benutzer löschen"
+
+#: ../../../views/html/users/delete.tpl:4
+#, php-format
+msgid "Should the user “%s” (%s) really be deleted?"
+msgstr "Soll der Benutzer „%s“ (%s) wirklich gelöscht werden?"
+
+#: ../../../views/html/users/edit.tpl:2 ../../../views/html/users/user.tpl:4
+msgid "Edit user"
+msgstr "Benutzer bearbeiten"
+
+#: ../../../views/html/users/index.tpl:3
+msgid "Create new user"
+msgstr "Neuen Benutzer erstellen"
+
+#: ../../../views/html/users/index.tpl:10 ../../../views/html/users/user.tpl:8
+#, php-format
+msgid "registered on %s"
+msgstr "registriert am %s"
+
+#: ../../../views/html/users/user.tpl:18
+msgid "Roles"
+msgstr "Rollen"
+
+#~ msgid "as"
+#~ msgstr "als"
+
+#~ msgid "containing optional Quests"
+#~ msgstr "Enthaltene optionale Quests"
+
+#~ msgid "This Quest is optional"
+#~ msgstr "Diese Quest ist optional"
+
+#~ msgid "created by %s on %s at %s"
+#~ msgstr "erstellt von %s am %s um %s Uhr"
+
+#~ msgid "registered on"
+#~ msgstr "registriert seit"
diff --git a/logs/empty b/logs/empty
new file mode 100644
index 00000000..e69de29b
diff --git a/media/empty b/media/empty
new file mode 100644
index 00000000..e69de29b
diff --git a/models/AchievementsModel.inc b/models/AchievementsModel.inc
new file mode 100644
index 00000000..760c9f06
--- /dev/null
+++ b/models/AchievementsModel.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\models;
+
+
+ /**
+ * Model to interact with Achievements-tables.
+ *
+ * @author Oliver Hanraths
+ */
+ class AchievementsModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new AchievementsModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ }
+
+?>
diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc
new file mode 100644
index 00000000..ce6e8b61
--- /dev/null
+++ b/models/CharactergroupsModel.inc
@@ -0,0 +1,147 @@
+
+ * @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;
+
+
+ /**
+ * Model of the CharactergroupsAgent to interact with
+ * Charactergroups-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new CharactergroupsModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get Character groups-groups of a Seminary.
+ *
+ * @param int $seminaryId ID of the corresponding Seminary
+ * @return array Character groups-groups data
+ */
+ public function getGroupsroupsForSeminary($seminaryId)
+ {
+ return $this->db->query(
+ 'SELECT id, name, url '.
+ 'FROM charactergroupsgroups '.
+ 'WHERE seminary_id = ?',
+ 'i',
+ $seminaryId
+ );
+ }
+
+
+ /**
+ * Get a Character groups-group by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $seminaryId ID of the corresponding Seminary
+ * @param string $groupsgroupUrl URL-name of the Character groups-group
+ * @return array Character groups-group data
+ */
+ public function getGroupsgroupByUrl($seminaryId, $groupsgroupUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT id, name, url '.
+ 'FROM charactergroupsgroups '.
+ 'WHERE seminary_id = ? AND url = ?',
+ 'is',
+ $seminaryId, $groupsgroupUrl
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($groupsgroupUrl);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get Character groups for a Character groups-group.
+ *
+ * @param int $groupsgroupId ID of the Character groups-group
+ * @return array Character groups
+ */
+ public function getGroupsForGroupsgroup($groupsgroupId)
+ {
+ return $this->db->query(
+ 'SELECT id, name, url, xps '.
+ 'FROM v_charactergroups '.
+ 'WHERE charactergroupsgroup_id = ?',
+ 'i',
+ $groupsgroupId
+ );
+ }
+
+
+ /**
+ * Get Character groups for a Character.
+ *
+ * @param int $characterId ID of the Character
+ * @return array Character groups
+ */
+ public function getGroupsForCharacter($characterId)
+ {
+ return $this->db->query(
+ 'SELECT charactergroups.id, charactergroups.charactergroupsgroup_id, charactergroups.name, charactergroups.url, charactergroups.xps, charactergroupsgroups.id AS charactergroupsgroup_id, charactergroupsgroups.name AS charactergroupsgroup_name, charactergroupsgroups.url AS charactergroupsgroup_url '.
+ 'FROM characters_charactergroups '.
+ 'LEFT JOIN v_charactergroups AS charactergroups ON charactergroups.id = characters_charactergroups.charactergroup_id '.
+ 'LEFT JOIN charactergroupsgroups ON charactergroupsgroups.id = charactergroups.charactergroupsgroup_id '.
+ 'WHERE characters_charactergroups.character_id = ?',
+ 'i',
+ $characterId
+ );
+ }
+
+
+ /**
+ * Get a Character group by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $groupsgroupId ID of the Character groups-group
+ * @param string $groupUrl URL-name of the Character group
+ * @return array Character group data
+ */
+ public function getGroupByUrl($groupsgroupId, $groupUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT id, name, url, xps '.
+ 'FROM v_charactergroups '.
+ 'WHERE charactergroupsgroup_id = ? AND url = ?',
+ 'is',
+ $groupsgroupId, $groupUrl
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($groupUrl);
+ }
+
+
+ return $data[0];
+ }
+
+ }
+
+?>
diff --git a/models/CharactergroupsquestsModel.inc b/models/CharactergroupsquestsModel.inc
new file mode 100644
index 00000000..eed514dd
--- /dev/null
+++ b/models/CharactergroupsquestsModel.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\models;
+
+
+ /**
+ * Model of the CharactergroupsquestsAgent to interact with
+ * Charactergroupsquests-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactergroupsquestsModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new CharactergroupsquestsModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get Character groups Quests of a Character groups-groups.
+ *
+ * @param int $groupsgroupId ID of the Character groups-group
+ * @return array Character groups Quest data
+ */
+ public function getQuestsForCharactergroupsgroup($groupsgroupId)
+ {
+ return $this->db->query(
+ 'SELECT id, questgroups_id, title, url '.
+ 'FROM charactergroupsquests '.
+ 'WHERE charactergroupsgroup_id = ?',
+ 'i',
+ $groupsgroupId
+ );
+ }
+
+
+ /**
+ * Get a Character groups Quest by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $groupsgroupId ID of the Character groups-group
+ * @param string $questUrl URL-title of the Character groups Quest
+ * @return array Character groups Quest data
+ */
+ public function getQuestByUrl($groupsgroupId, $questUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT id, questgroups_id, title, url, description, xps, rules, won_text, lost_text, questsmedia_id '.
+ 'FROM charactergroupsquests '.
+ 'WHERE charactergroupsgroup_id = ? AND url = ?',
+ 'is',
+ $groupsgroupId,
+ $questUrl
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questUrl);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get the Character groups for a Quest.
+ *
+ * @param int $questId ID of the Character groups Quest
+ * @return array Character groups
+ */
+ public function getGroupsForQuest($questId)
+ {
+ $groups = $this->db->query(
+ 'SELECT charactergroups.id, charactergroups.name, charactergroups.url, charactergroupsquests_groups.created, charactergroupsquests_groups.xps_factor, charactergroupsquests.xps '.
+ 'FROM charactergroupsquests_groups '.
+ 'LEFT JOIN charactergroups ON charactergroups.id = charactergroupsquests_groups.charactergroup_id '.
+ 'LEFT JOIN charactergroupsquests ON charactergroupsquests.id = charactergroupsquests_groups.charactergroupsquest_id '.
+ 'WHERE charactergroupsquests_groups.charactergroupsquest_id = ?',
+ 'i',
+ $questId
+ );
+ foreach($groups as &$group) {
+ $group['xps'] = round($group['xps'] * $group['xps_factor'], 1);
+ }
+
+
+ return $groups;
+ }
+
+
+ /**
+ * Get Character groups Quests for a Character group.
+ *
+ * @param int $groupId ID of the Character group
+ * @return array Character groups Quests
+ */
+ public function getQuestsForGroup($groupId)
+ {
+ $quests = $this->db->query(
+ 'SELECT charactergroupsquests.id, charactergroupsquests_groups.created, charactergroupsquests.title, charactergroupsquests.url, charactergroupsquests.xps, charactergroupsquests_groups.xps_factor '.
+ 'FROM charactergroupsquests_groups '.
+ 'LEFT JOIN charactergroupsquests ON charactergroupsquests.id = charactergroupsquests_groups.charactergroupsquest_id '.
+ 'WHERE charactergroupsquests_groups.charactergroup_id = ?',
+ 'i',
+ $groupId
+ );
+ foreach($quests as &$quest) {
+ $quest['group_xps'] = round($quest['xps'] * $quest['xps_factor'], 1);
+ }
+
+
+ return $quests;
+ }
+
+
+ }
+
+?>
diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc
new file mode 100644
index 00000000..a0f27fbe
--- /dev/null
+++ b/models/CharactersModel.inc
@@ -0,0 +1,209 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with Characters-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class CharactersModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new CharactersModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get all characters for an user.
+ *
+ * @param int $userId ID of the user
+ * @return array Characters
+ */
+ public function getCharactersForUser($userId)
+ {
+ return $this->db->query(
+ 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_url, seminaries.title AS seminary_title, seminaries.url AS seminary_url '.
+ 'FROM v_characters AS characters '.
+ 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '.
+ 'LEFT JOIN seminaries ON seminaries.id = charactertypes.seminary_id '.
+ 'WHERE user_id = ?',
+ 'i',
+ $userId
+ );
+ }
+
+
+ /**
+ * Get Characters for a Seminary.
+ *
+ * @param int $seminaryId ID of the Seminary
+ * @return array Characters
+ */
+ public function getCharactersForSeminary($seminaryId)
+ {
+ return $this->db->query(
+ 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_url, seminaries.title AS seminary_title, seminaries.url AS seminary_url '.
+ 'FROM v_characters AS characters '.
+ 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '.
+ 'LEFT JOIN seminaries ON seminaries.id = charactertypes.seminary_id '.
+ 'WHERE seminaries.id = ?',
+ 'i',
+ $seminaryId
+ );
+ }
+
+
+ /**
+ * Get Characters for a Character group.
+ *
+ * @param int $groupId ID of the Character group
+ * @return array Characters
+ */
+ public function getCharactersForGroup($groupId)
+ {
+ return $this->db->query(
+ 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters_charactergroups.is_leader, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '.
+ 'FROM v_characters AS characters '.
+ 'LEFT JOIN characters_charactergroups ON characters_charactergroups.character_id = characters.id '.
+ 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '.
+ 'WHERE characters_charactergroups.charactergroup_id = ?',
+ 'i',
+ $groupId
+ );
+ }
+
+
+ /**
+ * Get the character of a user for a Seminary.
+ *
+ * @throws IdNotFoundException
+ * @param int $userId ID of the user
+ * @param int $seminaryId ID of the Seminary
+ * @return array Character data
+ */
+ public function getCharacterForUserAndSeminary($userId, $seminaryId)
+ {
+ $data = $this->db->query(
+ 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '.
+ 'FROM v_characters AS characters '.
+ 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '.
+ 'WHERE characters.user_id = ? AND charactertypes.seminary_id = ?',
+ 'ii',
+ $userId, $seminaryId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($userId);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get a Character by its Url.
+ *
+ * @throws IdNotFoundException
+ * @param int $seminaryId ID of the Seminary
+ * @param string $characterUrl URL-name of the Character
+ * @return array Character data
+ */
+ public function getCharacterByUrl($seminaryId, $characterUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, media.url AS avatar_url, media.description AS avatar_description '.
+ 'FROM v_characters AS characters '.
+ 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '.
+ 'LEFT JOIN avatars ON avatars.id = characters.avatar_id '.
+ 'LEFT JOIN avatarpictures ON avatarpictures.media_id = avatars.avatarpicture_id '.
+ 'LEFT JOIN media ON media.id = avatarpictures.media_id '.
+ 'WHERE charactertypes.seminary_id = ? AND characters.url = ?',
+ 'is',
+ $seminaryId, $characterUrl
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($characterUrl);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get a Character by its Id.
+ *
+ * @throws IdNotFoundException
+ * @param string $characterId ID of the Character
+ * @return array Character data
+ */
+ public function getCharacterById($characterId)
+ {
+ $data = $this->db->query(
+ 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, media.url AS avatar_url, media.description AS avatar_description '.
+ 'FROM v_characters AS characters '.
+ 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '.
+ 'LEFT JOIN avatars ON avatars.id = characters.avatar_id '.
+ 'LEFT JOIN avatarpictures ON avatarpictures.media_id = avatars.avatarpicture_id '.
+ 'LEFT JOIN media ON media.id = avatarpictures.media_id '.
+ 'WHERE characters.id = ?',
+ 'i',
+ $characterId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($characterUrl);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get the XP-level of a Character.
+ *
+ * @param string $characterId ID of the Character
+ * @return array XP-level of Character
+ */
+ public function getXPLevelOfCharacters($characterId)
+ {
+ $data = $this->db->query(
+ 'SELECT xplevels.xps, xplevels.level, xplevels.name '.
+ 'FROM v_charactersxplevels '.
+ 'INNER JOIN xplevels ON xplevels.id = v_charactersxplevels.xplevel_id '.
+ 'WHERE v_charactersxplevels.character_id = ?',
+ 'i',
+ $characterId
+ );
+ if(!empty($data)) {
+ return $data[0];
+ }
+
+
+ return null;
+ }
+
+ }
+
+?>
diff --git a/models/DatabaseModel.inc b/models/DatabaseModel.inc
new file mode 100644
index 00000000..51259f4f
--- /dev/null
+++ b/models/DatabaseModel.inc
@@ -0,0 +1,83 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\models;
+
+
+ /**
+ * Default implementation of a database model.
+ *
+ * @author coderkun
+ */
+ class DatabaseModel extends \nre\core\Model
+ {
+ /**
+ * Database connection
+ *
+ * @static
+ * @var DatabaseDriver
+ */
+ protected $db = NULL;
+
+
+
+
+ /**
+ * Construct a new datamase model.
+ *
+ * @throws DatamodelException
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @param string $type Database type
+ * @param array $config Connection settings
+ */
+ function __construct($type, $config)
+ {
+ parent::__construct();
+
+ // Load database driver
+ $this->loadDriver($type);
+
+ // Establish database connection
+ $this->connect($type, $config);
+ }
+
+
+
+
+ /**
+ * Load the database driver.
+ *
+ * @throws DriverNotFoundException
+ * @throws DriverNotValidException
+ * @param string $driverName Name of the database driver
+ */
+ private function loadDriver($driverName)
+ {
+ \nre\core\Driver::load($driverName);
+ }
+
+
+ /**
+ * Establish a connection to the database.
+ *
+ * @throws DatamodelException
+ * @param string $driverName Name of the database driver
+ * @param array $config Connection settings
+ */
+ private function connect($driverName, $config)
+ {
+ $this->db = \nre\core\Driver::factory($driverName, $config);
+ }
+
+ }
+
+?>
diff --git a/models/MediaModel.inc b/models/MediaModel.inc
new file mode 100644
index 00000000..6165f7df
--- /dev/null
+++ b/models/MediaModel.inc
@@ -0,0 +1,90 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with the Media-tables.
+ *
+ * @author Oliver Hanraths
+ */
+ class MediaModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new MediaModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get a Medium by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $seminaryId ID of the seminary
+ * @param string $mediaURL URL-name of the Medium
+ * @return array Medium data
+ */
+ public function getMediaByUrl($seminaryId, $mediaUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT id, name, url, description, mimetype '.
+ 'FROM media '.
+ 'WHERE media.url = ?',
+ 's',
+ $mediaUrl
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($mediaUrl);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get a Medium by its ID.
+ *
+ * @throws IdNotFoundException
+ * @param int $seminaryId ID of the seminary
+ * @param int $mediaId ID of the Medium
+ * @return array Medium data
+ */
+ public function getMediaById($mediaId)
+ {
+ $data = $this->db->query(
+ 'SELECT id, name, url, description, mimetype '.
+ 'FROM media '.
+ 'WHERE media.id = ?',
+ 'i',
+ $mediaId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($mediaId);
+ }
+
+
+ return $data[0];
+ }
+
+ }
+
+?>
diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc
new file mode 100644
index 00000000..7f5e466b
--- /dev/null
+++ b/models/QuestgroupsModel.inc
@@ -0,0 +1,424 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with Questgroups-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupsModel extends \hhu\z\Model
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('questgroupshierarchy', 'quests');
+
+
+
+
+ /**
+ * Construct a new QuestgroupsModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get all Questgroups for a Questgroup hierarchy.
+ *
+ * @param int $hierarchyId ID of the Questgroup hierarchy to get Questgroups for
+ * @param int $parentQuestgroupId ID of the parent Questgroup hierarchy
+ * @return array Questgroups for the given hierarchy
+ */
+ public function getQuestgroupsForHierarchy($hierarchyId, $parentQuestgroupId=null)
+ {
+ // Get Questgroups
+ $questgroups = array();
+ if(is_null($parentQuestgroupId))
+ {
+ $questgroups = $this->db->query(
+ 'SELECT id, questgroupshierarchy_id, pos, title, url, questgroupspicture_id '.
+ 'FROM questgroups '.
+ 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id IS NULL '.
+ 'ORDER BY questgroups.pos ASC',
+ 'i',
+ $hierarchyId
+ );
+ }
+ else
+ {
+ $questgroups = $this->db->query(
+ 'SELECT id, questgroupshierarchy_id, pos, title, url, questgroupspicture_id '.
+ 'FROM questgroups '.
+ 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id = ? '.
+ 'ORDER BY questgroups.pos ASC',
+ 'ii',
+ $hierarchyId, $parentQuestgroupId
+ );
+ }
+
+ // Add additional data
+ foreach($questgroups as &$questgroup)
+ {
+ // Total XPs
+ $questgroup['xps'] = $this->getAchievableXPsForQuestgroup($questgroup['id']);
+ }
+
+
+ // Return Questgroups
+ return $questgroups;
+ }
+
+
+ /**
+ * Get a Questgroup by its ID.
+ *
+ * @throws IdNotFoundException
+ * @param int $questgroupId ID of a Questgroup
+ * @return array Questgroup data
+ */
+ public function getQuestgroupById($questgroupId)
+ {
+ $data = $this->db->query(
+ 'SELECT id, questgroupshierarchy_id, parent_questgroup_id, pos, title, url, questgroupspicture_id '.
+ 'FROM questgroups '.
+ 'WHERE questgroups.id = ?',
+ 'i',
+ $questgroupId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questgroupId);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get a Questgroup by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $seminaryId ID of the corresponding seminary
+ * @param string $questgroupURL URL-title of a Questgroup
+ * @return array Questgroup data
+ */
+ public function getQuestgroupByUrl($seminaryId, $questgroupUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT questgroups.id, questgroups.questgroupshierarchy_id, questgroups.parent_questgroup_id, questgroups.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '.
+ 'FROM questgroups '.
+ 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '.
+ 'WHERE questgroupshierarchy.seminary_id = ? AND questgroups.url = ?',
+ 'is',
+ $seminaryId, $questgroupUrl
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questgroupUrl);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get texts of a Questgroup.
+ *
+ * @param int $questgroupId ID of a Questgroup
+ * @return array Texts of this Questgroup
+ */
+ public function getQuestgroupTexts($questgroupId)
+ {
+ return $this->db->query(
+ 'SELECT id, pos, text '.
+ 'FROM questgrouptexts '.
+ 'WHERE questgroup_id = ? '.
+ 'ORDER BY pos ASC',
+ 'i',
+ $questgroupId
+ );
+ }
+
+
+ /**
+ * Get first texts of a Questgroup.
+ *
+ * @param int $questgroupId ID of a Questgroup
+ * @return array First Text of this Questgroup
+ */
+ public function getFirstQuestgroupText($questgroupId)
+ {
+ $data = $this->db->query(
+ 'SELECT id, pos, text '.
+ 'FROM questgrouptexts '.
+ 'WHERE questgroup_id = ? '.
+ 'ORDER BY pos ASC '.
+ 'LIMIT 1',
+ 'i',
+ $questgroupId
+ );
+ if(!empty($data)) {
+ return $data[0];
+ }
+
+
+ return null;
+ }
+
+
+ /**
+ * Get the next Questgroup.
+ *
+ * Determine the next Questgroup. If there is no next Questgroup
+ * on the same level as the given Quest then the followed-up
+ * Questgroup from a higher hierarchy level is returned.
+ *
+ * @param int $questgroupId ID of Questgroup to get next Questgroup of
+ * @return array Questgroup data
+ */
+ public function getNextQuestgroup($questgroupId)
+ {
+ $currentQuestgroup = $this->getQuestgroupById($questgroupId);
+ $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['parent_questgroup_id'], $currentQuestgroup['pos']);
+ while(is_null($nextQuestgroup) && !is_null($currentQuestgroup['parent_questgroup_id']))
+ {
+ $currentQuestgroup = $this->getQuestgroupById($currentQuestgroup['parent_questgroup_id']);
+ $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['parent_questgroup_id'], $currentQuestgroup['pos']);
+ }
+
+
+ return $nextQuestgroup;
+ }
+
+
+ /**
+ * Get the previous Questgroup.
+ *
+ * Determine the previous Questgroup. If there is no previous
+ * Questgroup on the same level as the given Quest then the
+ * followed-up Questgroup from a higher hierarchy level is
+ * returned.
+ *
+ * @param int $questgroupId ID of Questgroup to get previous Questgroup of
+ * @return array Questgroup data
+ */
+ public function getPreviousQuestgroup($questgroupId)
+ {
+ $currentQuestgroup = $this->getQuestgroupById($questgroupId);
+ $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['parent_questgroup_id'], $currentQuestgroup['pos']);
+ while(is_null($previousQuestgroup) && !is_null($currentQuestgroup['parent_questgroup_id']))
+ {
+ $currentQuestgroup = $this->getQuestgroupById($currentQuestgroup['parent_questgroup_id']);
+ $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['parent_questgroup_id'], $currentQuestgroup['pos']);
+ }
+
+
+ return $previousQuestgroup;
+ }
+
+
+ /**
+ * Determine if the given Character has solved the Quests form
+ * this Questgroup.
+ *
+ * @param int $questgroupId ID of Questgroup to check
+ * @param int $characterId ID of Character to check
+ * @result boolean Whether Character has solved the Questgroup or not
+ */
+ public function hasCharacterSolvedQuestgroup($questgroupId, $characterId)
+ {
+ $currentQuestgroup = $this->getQuestgroupById($questgroupId);
+ $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($currentQuestgroup['questgroupshierarchy_id']);
+ $lastChildQuestgroupshierarchy = array_pop($childQuestgroupshierarchy);
+ while(!is_null($lastChildQuestgroupshierarchy))
+ {
+ $questgroups = $this->getQuestgroupsForHierarchy($lastChildQuestgroupshierarchy['id'], $currentQuestgroup['id']);
+ $currentQuestgroup = array_pop($questgroups);
+ $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($currentQuestgroup['questgroupshierarchy_id']);
+ $lastChildQuestgroupshierarchy = array_pop($childQuestgroupshierarchy);
+ }
+
+ $quests = $this->Quests->getMainquestsForQuestgroup($currentQuestgroup['id']);
+ $lastQuest = array_pop($quests);
+
+
+ return $this->Quests->hasCharacterSolvedQuest($lastQuest['id'], $characterId);
+ }
+
+
+ /**
+ * Summarize XPs of all Quests for a Questgroup and its
+ * sub-Questgroups.
+ *
+ * @param int $questgroupId ID of Questgroup
+ * @return Sum of XPs
+ */
+ public function getAchievableXPsForQuestgroup($questgroupId)
+ {
+ // Sum of XPs
+ $xps = 0;
+
+ // Current Questgroup
+ $questgroup = $this->getQuestgroupById($questgroupId);
+
+ // Quests of current Questgroup
+ $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']);
+ foreach($quests as &$quest) {
+ $xps += $quest['xps'];
+ }
+
+ // XPs of child Questgroups
+ $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['questgroupshierarchy_id']);
+ foreach($childQuestgroupshierarchy as &$hierarchy)
+ {
+ $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']);
+ foreach($questgroups as &$questgroup) {
+ $xps += $this->getAchievableXPsForQuestgroup($questgroup['id']);
+ }
+ }
+
+
+ // Return summarized XPs
+ return $xps;
+ }
+
+
+ /**
+ * Summarize XPs of all Quests for a Questgroup and its
+ * sub-Questgroups solved by a Character.
+ *
+ * @param int $questgroupId ID of Questgroup
+ * @param int $characterId ID of Character
+ * @return Sum of XPs
+ */
+ public function getAchievedXPsForQuestgroup($questgroupId, $characterId)
+ {
+ // Sum of XPs
+ $xps = 0;
+
+ // Current Questgroup
+ $questgroup = $this->getQuestgroupById($questgroupId);
+
+ // Quests of current Questgroup
+ $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']);
+ foreach($quests as &$quest)
+ {
+ if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) {
+ $xps += $quest['xps'];
+ }
+ }
+
+ // XPs of child Questgroups
+ $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['questgroupshierarchy_id']);
+ foreach($childQuestgroupshierarchy as &$hierarchy)
+ {
+ $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']);
+ foreach($questgroups as &$questgroup) {
+ $xps += $this->getAchievedXPsForQuestgroup($questgroup['id'], $characterId);
+ }
+ }
+
+
+ // Return summarized XPs
+ return $xps;
+ }
+
+
+
+
+ /**
+ * Get the next (direct) Questgroup.
+ *
+ * @param int $parentQuestgroupId ID of parent Questgroup to get next Questgroup of
+ * @param int $questgroupPos Position of Questgroup to get next Questgroup of
+ * @return array Data of next Questgroup or NULL
+ */
+ private function _getNextQuestgroup($parentQuestgroupId, $questgroupPos)
+ {
+ if(!is_null($parentQuestgroupId))
+ {
+ $data = $this->db->query(
+ 'SELECT * '.
+ 'FROM questgroups '.
+ 'WHERE parent_questgroup_id = ? AND pos = ? + 1',
+ 'ii',
+ $parentQuestgroupId, $questgroupPos
+ );
+ }
+ else
+ {
+ $data = $this->db->query(
+ 'SELECT * '.
+ 'FROM questgroups '.
+ 'WHERE parent_questgroup_id IS NULL AND pos = ? + 1',
+ 'i',
+ $questgroupPos
+ );
+ }
+ if(empty($data)) {
+ return null;
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get the previous (direct) Questgroup.
+ *
+ * @param int $parentQuestgroupId ID of parent Questgroup to get previous Questgroup of
+ * @param int $questgroupPos Position of Questgroup to get previous Questgroup of
+ * @return array Data of previous Questgroup or NULL
+ */
+ private function _getPreviousQuestgroup($parentQuestgroupId, $questgroupPos)
+ {
+ if(!is_null($parentQuestgroupId))
+ {
+ $data = $this->db->query(
+ 'SELECT * '.
+ 'FROM questgroups '.
+ 'WHERE parent_questgroup_id = ? AND pos = ? - 1',
+ 'ii',
+ $parentQuestgroupId, $questgroupPos
+ );
+ }
+ else
+ {
+ $data = $this->db->query(
+ 'SELECT * '.
+ 'FROM questgroups '.
+ 'WHERE parent_questgroup_id IS NULL AND pos = ? - 1',
+ 'i',
+ $questgroupPos
+ );
+ }
+ if(empty($data)) {
+ return null;
+ }
+
+
+ return $data[0];
+ }
+
+ }
+
+?>
diff --git a/models/QuestgroupshierarchyModel.inc b/models/QuestgroupshierarchyModel.inc
new file mode 100644
index 00000000..df676ea4
--- /dev/null
+++ b/models/QuestgroupshierarchyModel.inc
@@ -0,0 +1,103 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with Questgroupshierarchy-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestgroupshierarchyModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new QuestgroupshierarchyModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get a Questgroup hierarchy by its ID.
+ *
+ * throws IdNotFoundException
+ * @param int $questgroupshierarchyId ID of a Questgroup hierarchy
+ * @return array Questgroup hierarchy
+ */
+ public function getHierarchyById($questgroupshierarchyId)
+ {
+ $data = $this->db->query(
+ 'SELECT id, seminary_id, parent_questgroupshierarchy_id, pos, title_singular, title_plural, url '.
+ 'FROM questgroupshierarchy '.
+ 'WHERE questgroupshierarchy.id = ?',
+ 'i',
+ $questgroupshierarchyId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questgroupshierarchyId);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get the toplevel hierarchy entries of a Seminary.
+ *
+ * @param int $seminaryId ID of the seminary to get hierarchy for
+ * @return array Toplevel hierarchy
+ */
+ public function getHierarchyForSeminary($seminaryId)
+ {
+ return $this->db->query(
+ 'SELECT id, seminary_id, parent_questgroupshierarchy_id, pos, title_singular, title_plural, url '.
+ 'FROM questgroupshierarchy '.
+ 'WHERE '.
+ 'questgroupshierarchy.seminary_id = ? AND '.
+ 'questgroupshierarchy.parent_questgroupshierarchy_id IS NULL '.
+ 'ORDER BY questgroupshierarchy.pos ASC',
+ 'i',
+ $seminaryId
+ );
+ }
+
+
+ /**
+ * Get the child hierarchy entries of a Questgroup hierarchy.
+ *
+ * @param int $questgroupshierarchyId ID of a Questgroup hierarchy
+ * @return array Child Questgroup hierarchy entries
+ */
+ public function getChildQuestgroupshierarchy($questgroupshierarchyId)
+ {
+ return $this->db->query(
+ 'SELECT id, seminary_id, parent_questgroupshierarchy_id, pos, title_singular, title_plural, url '.
+ 'FROM questgroupshierarchy '.
+ 'WHERE questgroupshierarchy.parent_questgroupshierarchy_id = ? '.
+ 'ORDER BY questgroupshierarchy.pos ASC',
+ 'i',
+ $questgroupshierarchyId
+ );
+ }
+
+ }
+
+?>
diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc
new file mode 100644
index 00000000..b93edfb1
--- /dev/null
+++ b/models/QuestsModel.inc
@@ -0,0 +1,344 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with Quests-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuestsModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new QuestsModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get all Quests for the given Questgroup.
+ *
+ * @param int $questgroupId ID of a Questgroup
+ * @return array Quests of the given Questgroup
+ */
+ public function getMainquestsForQuestgroup($questgroupId)
+ {
+ return $this->db->query(
+ 'SELECT id, questtype_id, title, url, xps, task '.
+ 'FROM quests '.
+ 'INNER JOIN mainquests ON mainquests.quest_id = quests.id '.
+ 'WHERE questgroup_id = ?',
+ 'i',
+ $questgroupId
+ );
+ }
+
+
+ /**
+ * Get a Quest and its data by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $seminaryId ID of the corresponding Seminary
+ * @param int $questgroupId ID of the corresponding Questgroup
+ * @param string $questURL URL-title of a Quest
+ * @return array Quest data
+ */
+ public function getQuestByUrl($seminaryId, $questgroupId, $questUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id, ('.
+ 'SELECT count(mainquests.quest_id) FROM mainquests WHERE mainquests.quest_id = quests.id) AS is_mainquest '.
+ 'FROM quests '.
+ 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '.
+ 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '.
+ 'WHERE questgroupshierarchy.seminary_id = ? AND questgroups.id = ? AND quests.url = ?',
+ 'iis',
+ $seminaryId, $questgroupId, $questUrl
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questUrl);
+ }
+
+ $data[0]['is_mainquest'] = ($data[0]['is_mainquest'] == 1);
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get a Quest and its data by its ID.
+ *
+ * @throws IdNotFoundException
+ * @param string $questId ID of a Quest
+ * @return array Quest data
+ */
+ public function getQuestById($questId)
+ {
+ $data = $this->db->query(
+ 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id, ('.
+ 'SELECT count(mainquests.quest_id) FROM mainquests WHERE mainquests.quest_id = quests.id) AS is_mainquest '.
+ 'FROM quests '.
+ 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '.
+ 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '.
+ 'WHERE quests.id = ?',
+ 'i',
+ $questId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questId);
+ }
+
+ $data[0]['is_mainquest'] = ($data[0]['is_mainquest'] == 1);
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get a Sidequest and its data by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $seminaryId ID of the corresponding Seminary
+ * @param int $questgroupId ID of the corresponding Questgroup
+ * @param int $questId ID of the Quest
+ * @param string $sidequestUrl URL-title of a Sidequest
+ * @return array Sidequest data
+ */
+ public function getSidequestByUrl($seminaryId, $questgroupId, $questId, $sidequestUrl)
+ {
+ $data = $this->db->query(
+ 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '.
+ 'FROM quests '.
+ 'INNER JOIN sidequests ON sidequests.quest_id = quests.id '.
+ 'LEFT JOIN questtexts ON questtexts.id = sidequests.questtext_id '.
+ 'LEFT JOIN mainquests ON mainquests.quest_id = questtexts.mainquest_id '.
+ 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '.
+ 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '.
+ 'LEFT JOIN seminaries ON seminaries.id = questgroupshierarchy.seminary_id '.
+ 'WHERE quests.url = ? AND mainquests.id = ? AND questgroups.id = ? AND seminaries.id = ?',
+ 'siii',
+ $sidequestUrl,
+ $questId,
+ $questgroupId,
+ $seminaryId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($sidequestUrl);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get all sidequests for a Quest.
+ *
+ * @param int $questId ID of the quest
+ * @return array Sidequests for the quest
+ */
+ public function getSidequestsForQuest($questId)
+ {
+ return $this->db->query(
+ 'SELECT quests.id, sidequests.questtext_id, quests.title, quests.url, sidequests.entry_text '.
+ 'FROM quests '.
+ 'INNER JOIN sidequests ON sidequests.quest_id = quests.id '.
+ 'LEFT JOIN questtexts ON questtexts.id = sidequests.questtext_id '.
+ 'WHERE questtexts.quest_id = ?',
+ 'i',
+ $questId
+ );
+ }
+
+
+ /**
+ * Get all sidequests for a Questtext.
+ *
+ * @param int $questtextId ID of the questtext
+ * @return array Sidequests for the questtext
+ */
+ public function getSidequestsForQuesttext($questtextId)
+ {
+ return $this->db->query(
+ 'SELECT id, questtext_id, title, url, entry_text '.
+ 'FROM quests '.
+ 'INNER JOIN sidequests ON sidequests.quest_id = quests.id '.
+ 'WHERE questtext_id = ?',
+ 'i',
+ $questtextId
+ );
+ }
+
+
+ /**
+ * Get Quests that follow-up a Quest.
+ *
+ * @param int $questId ID of Quest to get next Quests of
+ * @return array Quests data
+ */
+ public function getNextQuests($questId)
+ {
+ return $this->db->query(
+ 'SELECT quests.id, quests.title, quests.url, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '.
+ 'FROM mainquests_previousmainquests '.
+ 'LEFT JOIN mainquests ON mainquests.quest_id = mainquests_previousmainquests.mainquest_id '.
+ 'INNER JOIN quests ON quests.id = mainquests.quest_id '.
+ 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '.
+ 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '.
+ 'WHERE mainquests_previousmainquests.previous_mainquest_id = ?',
+ 'i',
+ $questId
+ );
+ }
+
+
+ /**
+ * Get Quests that the given Quests follows-up to.
+ *
+ * @param int $questId ID of Quest to get previous Quests of
+ * @return array Quests data
+ */
+ public function getPreviousQuests($questId)
+ {
+ return $this->db->query(
+ 'SELECT quests.id, quests.title, quests.url, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '.
+ 'FROM mainquests_previousmainquests '.
+ 'LEFT JOIN mainquests ON mainquests.quest_id = mainquests_previousmainquests.previous_mainquest_id '.
+ 'INNER JOIN quests ON quests.id = mainquests.quest_id '.
+ 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '.
+ 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '.
+ 'WHERE mainquests_previousmainquests.mainquest_id = ?',
+ 'i',
+ $questId
+ );
+ }
+
+
+ /**
+ * Mark a Quest as solved for a Character.
+ *
+ * @param int $questId ID of Quest to mark as solved
+ * @param int $characterId ID of Character that solved the Quest
+ */
+ public function setQuestSolved($questId, $characterId)
+ {
+ $this->db->query(
+ 'INSERT INTO quests_characters '.
+ '(quest_id, character_id, status) '.
+ 'VALUES '.
+ '(?, ?, ?)',
+ 'iii',
+ $questId,
+ $characterId,
+ 0
+ );
+ }
+
+
+ /**
+ * Mark a Quest as unsolved for a Character.
+ *
+ * @param int $questId ID of Quest to mark as unsolved
+ * @param int $characterId ID of Character that unsolved the Quest
+ */
+ public function setQuestUnsolved($questId, $characterId)
+ {
+ $this->db->query(
+ 'INSERT INTO quests_characters '.
+ '(quest_id, character_id, status) '.
+ 'VALUES '.
+ '(?, ?, ?)',
+ 'iii',
+ $questId,
+ $characterId,
+ -1
+ );
+ }
+
+
+ /**
+ * Determine if the given Character has solved the given Quest.
+ *
+ * @param int $questId ID of Quest to check
+ * @param int $characterId ID of Character to check
+ * @result boolean Whether Character has solved the Quest or not
+ */
+ public function hasCharacterSolvedQuest($questId, $characterId)
+ {
+ $count = $this->db->query(
+ 'SELECT count(id) AS c '.
+ 'FROM quests_characters '.
+ 'WHERE quest_id = ? AND character_id = ? AND status = 0',
+ 'ii',
+ $questId,
+ $characterId
+ );
+
+
+ return (!empty($count) && intval($count[0]['c']) > 0);
+ }
+
+
+ /**
+ * Get Characters that solved a Quest.
+ *
+ * @param int $questId ID of Quest to get Characters for
+ * @return array Characters data
+ */
+ public function getCharactersSolvedQuest($questId)
+ {
+ return $data = $this->db->query(
+ 'SELECT character_id, created, text '.
+ 'FROM questtypes_submit_characters '.
+ 'WHERE quest_id = ? AND EXISTS ('.
+ 'SELECT character_id FROM quests_characters WHERE quest_id = questtypes_submit_characters.quest_id AND status = 0'.
+ ')',
+ 'i',
+ $questId
+ );
+ }
+
+
+ /**
+ * Get Characters that did not solved a Quest.
+ *
+ * @param int $questId ID of Quest to get Characters for
+ * @return array Characters data
+ */
+ public function getCharactersUnsolvedQuest($questId)
+ {
+ return $data = $this->db->query(
+ 'SELECT character_id, created, text '.
+ 'FROM questtypes_submit_characters '.
+ 'WHERE quest_id = ? AND NOT EXISTS ('.
+ 'SELECT character_id FROM quests_characters WHERE quest_id = questtypes_submit_characters.quest_id AND status = 0'.
+ ')',
+ 'i',
+ $questId
+ );
+ }
+
+ }
+
+?>
diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc
new file mode 100644
index 00000000..38a1286d
--- /dev/null
+++ b/models/QuesttextsModel.inc
@@ -0,0 +1,135 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with Questtexts-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttextsModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new QuesttextsModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get a Questtext for a Quest by its URL.
+ *
+ * @throws IdNotFoundException
+ * @param int $questId ID of the Quest to get text for
+ * @param string $questtexttypeUrl URL of the Questtexttype
+ * @param int $pos Position of Questtexttype
+ * @return array Questtexttype data
+ */
+ public function getQuesttextByUrl($questId, $questtexttypeUrl, $pos)
+ {
+ $data = $this->db->query(
+ 'SELECT questtexts.id, questtexts.text, questtexts.pos, questtexts.out_text, questtexts.abort_text, questtexts.questsmedia_id, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '.
+ 'FROM questtexts '.
+ 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '.
+ 'WHERE questtexts.quest_id = ? AND questtexttypes.url = ? AND questtexts.pos = ?',
+ 'isi',
+ $questId, $questtexttypeUrl, $pos
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questtexttypeUrl);
+ }
+
+
+ return $data = $data[0];
+ }
+
+
+ /**
+ * Get count of Questtexts for a Quest.
+ *
+ * @param int $questId ID of the Quest
+ * @param string $questtexttypeUrl URL of the Questtexttype
+ * @return int Conut of Questtexts for Quest
+ */
+ public function getQuesttextsCountForQuest($questId, $questtexttypUrl)
+ {
+ $count = 0;
+ $data = $this->db->query(
+ 'SELECT COUNT(questtexts.id) AS c '.
+ 'FROM questtexts '.
+ 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '.
+ 'WHERE questtexts.quest_id = ? AND questtexttypes.url = ?',
+ 'is',
+ $questId, $questtexttypUrl
+ );
+ if(!empty($data)) {
+ $count = $data[0]['c'];
+ }
+
+
+ return $count;
+ }
+
+
+ /**
+ * Get corresponding Questtext for a Sidequest.
+ *
+ * @throws IdNotFoundException
+ * @param int $sidequestId ID of the Sidequest to get the Questtext for
+ * @param array Questtext data
+ */
+ public function getQuesttextForSidequest($sidequestId)
+ {
+ $data = $this->db->query(
+ 'SELECT questtexts.id, questtexts.text, questtexts.pos, questtexts.quest_id, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '.
+ 'FROM quests '.
+ 'INNER JOIN sidequests ON sidequests.quest_id = quests.id '.
+ 'LEFT JOIN questtexts ON questtexts.id = sidequests.questtext_id '.
+ 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '.
+ 'WHERE quests.id = ?',
+ 'i',
+ $sidequestId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($sidequestId);
+ }
+
+
+ return $data[0];
+ }
+
+
+ /**
+ * Get all registered Questtexttypes.
+ *
+ * @return array Registered Questtexttypes
+ */
+ public function getQuesttexttypes()
+ {
+ return $this->db->query(
+ 'SELECT id, type, url '.
+ 'FROM questtexttypes'
+ );
+ }
+
+ }
+
+?>
diff --git a/models/QuesttypesModel.inc b/models/QuesttypesModel.inc
new file mode 100644
index 00000000..3e0ef265
--- /dev/null
+++ b/models/QuesttypesModel.inc
@@ -0,0 +1,62 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with Questtypes-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class QuesttypesModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new QuesttypesModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get a Questtype by its ID
+ *
+ * @param int $questtypeId ID of Questtype
+ * @return array Questtype data
+ */
+ public function getQuesttypeById($questtypeId)
+ {
+ $data = $this->db->query(
+ 'SELECT title, classname '.
+ 'FROM questtypes '.
+ 'WHERE id = ?',
+ 'i',
+ $questtypeId
+ );
+ if(empty($data)) {
+ throw new \nre\exceptions\IdNotFoundException($questtypeId);
+ }
+
+
+ return $data = $data[0];
+ }
+
+ }
+
+?>
diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc
new file mode 100644
index 00000000..15963271
--- /dev/null
+++ b/models/SeminariesModel.inc
@@ -0,0 +1,161 @@
+
+ * @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;
+
+
+ /**
+ * Model of the SeminariesAgent to list registered seminaries.
+ *
+ * @author Oliver Hanraths
+ */
+ class SeminariesModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new SeminariesModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get registered seminaries.
+ *
+ * @return array Seminaries
+ */
+ public function getSeminaries()
+ {
+ // Get seminaries
+ return $this->db->query(
+ 'SELECT id, created, created_user_id, title, url, description '.
+ 'FROM seminaries '.
+ 'ORDER BY created DESC'
+ );
+ }
+
+
+ /**
+ * Get a seminary and its data by its ID.
+ *
+ * @throws IdNotFoundException
+ * @param string $seminaryId ID of a seminary
+ * @return array Seminary
+ */
+ public function getSeminaryById($seminaryId)
+ {
+ $seminary = $this->db->query(
+ 'SELECT id, created, created_user_id, title, url, description '.
+ 'FROM seminaries '.
+ 'WHERE id = ?',
+ 'i',
+ $seminaryId
+ );
+ if(empty($seminary)) {
+ throw new \nre\exceptions\IdNotFoundException($seminaryId);
+ }
+
+
+ return $seminary[0];
+ }
+
+
+ /**
+ * Get a seminary and its data by its URL-title.
+ *
+ * @throws IdNotFoundException
+ * @param string $seminaryUrl URL-Title of a seminary
+ * @return array Seminary
+ */
+ public function getSeminaryByUrl($seminaryUrl)
+ {
+ $seminary = $this->db->query(
+ 'SELECT id, created, created_user_id, title, url, description '.
+ 'FROM seminaries '.
+ 'WHERE url = ?',
+ 's',
+ $seminaryUrl
+ );
+ if(empty($seminary)) {
+ throw new \nre\exceptions\IdNotFoundException($seminaryUrl);
+ }
+
+
+ return $seminary[0];
+ }
+
+
+ /**
+ * Create a new seminary.
+ *
+ * @param string $title Title of seminary to create
+ * @param int $userId ID of creating user
+ * @return int ID of the newly created seminary
+ */
+ public function createSeminary($title, $userId)
+ {
+ $this->db->query(
+ 'INSERT INTO seminaries '.
+ '(created_user_id, title, url) '.
+ 'VALUES '.
+ '(?, ?, ?)',
+ 'iss',
+ $userId,
+ $title,
+ \nre\core\Linker::createLinkParam($title)
+ );
+
+
+ return $this->db->getInsertId();
+ }
+
+
+ /**
+ * Edit a seminary.
+ *
+ * @throws DatamodelException
+ * @param int $seminaryId ID of the seminary to delete
+ * @param string $title New title of seminary
+ */
+ public function editSeminary($seminaryId, $title)
+ {
+ $this->db->query(
+ 'UPDATE seminaries '.
+ 'SET title = ?, url = ? '.
+ 'WHERE id = ?',
+ 'ssi',
+ $title,
+ \nre\core\Linker::createLinkParam($title),
+ $seminaryId
+ );
+ }
+
+
+ /**
+ * Delete a seminary.
+ *
+ * @param int $seminaryId ID of the seminary to delete
+ */
+ public function deleteSeminary($seminaryId)
+ {
+ $this->db->query('DELETE FROM seminaries WHERE id = ?', 'i', $seminaryId);
+ }
+
+ }
+
+?>
diff --git a/models/SeminarycharacterfieldsModel.inc b/models/SeminarycharacterfieldsModel.inc
new file mode 100644
index 00000000..c881df2c
--- /dev/null
+++ b/models/SeminarycharacterfieldsModel.inc
@@ -0,0 +1,58 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with the Seminarycharacterfields-tables.
+ *
+ * @author Oliver Hanraths
+ */
+ class SeminarycharacterfieldsModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new SeminarycharacterfieldsModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get Seminary Character fields of a Character.
+ *
+ * @param int $characterId ID of the Character
+ * @return array Seminary Character fields
+ */
+ public function getFieldsForCharacter($characterId)
+ {
+ return $this->db->query(
+ 'SELECT seminarycharacterfields.title, characters_seminarycharacterfields.value '.
+ 'FROM characters_seminarycharacterfields '.
+ 'LEFT JOIN seminarycharacterfields ON seminarycharacterfields.id = characters_seminarycharacterfields.seminarycharacterfield_id '.
+ 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '.
+ 'WHERE characters_seminarycharacterfields.character_id = ?',
+ 'i',
+ $characterId
+ );
+ }
+
+ }
+
+?>
diff --git a/models/UserrolesModel.inc b/models/UserrolesModel.inc
new file mode 100644
index 00000000..33121a21
--- /dev/null
+++ b/models/UserrolesModel.inc
@@ -0,0 +1,77 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with userroles-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class UserrolesModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new UserrolesModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get all userroles for an user referenced by its ID.
+ *
+ * @param int $userId ID of an user
+ * @return array Userroles for an user
+ */
+ public function getUserrolesForUserById($userId)
+ {
+ return $this->db->query(
+ 'SELECT userroles.id, userroles.created, userroles.name '.
+ 'FROM users_userroles '.
+ 'LEFT JOIN userroles ON userroles.id = users_userroles.userrole_id '.
+ 'WHERE users_userroles.user_id = ?',
+ 'i',
+ $userId
+ );
+ }
+
+
+ /**
+ * Get all userroles for an user referenced by its URL-username.
+ *
+ * @param string $userUrl URL-Username of an user
+ * @return array Userroles for an user
+ */
+ public function getUserrolesForUserByUrl($userUrl)
+ {
+ return $this->db->query(
+ 'SELECT userroles.id, userroles.created, userroles.name '.
+ 'FROM users '.
+ 'LEFT JOIN users_userroles ON users_userroles.user_id = users.id '.
+ 'LEFT JOIN userroles ON userroles.id = users_userroles.userrole_id '.
+ 'WHERE users.url = ?',
+ 's',
+ $userUrl
+ );
+ }
+
+ }
+
+?>
diff --git a/models/UsersModel.inc b/models/UsersModel.inc
new file mode 100644
index 00000000..d42db7c7
--- /dev/null
+++ b/models/UsersModel.inc
@@ -0,0 +1,249 @@
+
+ * @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;
+
+
+ /**
+ * Model of the UsersAgent to list users and get their data.
+ *
+ * @author Oliver Hanraths
+ */
+ class UsersModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new UsersModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get registered users.
+ *
+ * @return array Users
+ */
+ public function getUsers()
+ {
+ return $this->db->query(
+ 'SELECT id, created, username, url, surname, prename, email '.
+ 'FROM users '.
+ 'ORDER BY username ASC'
+ );
+ }
+
+
+ /**
+ * Get a user and its data by its ID.
+ *
+ * @throws IdNotFoundException
+ * @param int $userId ID of an user
+ * @return array Userdata
+ */
+ public function getUserById($userId)
+ {
+ // Get user
+ $user = $this->db->query(
+ 'SELECT id, created, username, url, surname, prename, email '.
+ 'FROM users '.
+ 'WHERE id = ?',
+ 'i',
+ $userId
+ );
+ if(empty($user)) {
+ throw new \nre\exceptions\IdNotFoundException($userId);
+ }
+
+
+ return $user[0];
+ }
+
+
+ /**
+ * Get a user and its data by its URL-username.
+ *
+ * @throws IdNotFoundException
+ * @param string $userUrl URL-Username of an user
+ * @return array Userdata
+ */
+ public function getUserByUrl($userUrl)
+ {
+ // Get user
+ $user = $this->db->query(
+ 'SELECT id, created, username, url, surname, prename, email '.
+ 'FROM users '.
+ 'WHERE url = ?',
+ 's',
+ $userUrl
+ );
+ if(empty($user)) {
+ throw new \nre\exceptions\IdNotFoundException($userUrl);
+ }
+
+
+ return $user[0];
+ }
+
+
+ /**
+ * Log a user in if its credentials are valid.
+ *
+ * @throws DatamodelException
+ * @param string $username The name of the user to log in
+ * @param string $password Plaintext password of the user to log in
+ */
+ public function login($username, $password)
+ {
+ $data = $this->db->query('SELECT id, password FROM users WHERE username = ?', 's', $username);
+ if(!empty($data))
+ {
+ $data = $data[0];
+ if($this->verify($password, $data['password'])) {
+ return $data['id'];
+ }
+ }
+
+
+ return null;
+ }
+
+
+ /**
+ * Create a new user.
+ *
+ * @param string $username Username of the user to create
+ * @param string $email E‑Mail-Address of the user to create
+ * @param string $password Password of the user to create
+ * @return int ID of the newly created user
+ */
+ public function createUser($username, $email, $password)
+ {
+ $this->db->query(
+ 'INSERT INTO users '.
+ '(username, url, email, password) '.
+ 'VALUES '.
+ '(?, ?, ?, ?)',
+ 'ssss',
+ $username,
+ \nre\core\Linker::createLinkParam($username),
+ $email,
+ $this->hash($password)
+ );
+
+
+ return $this->db->getInsertId();
+ }
+
+
+ /**
+ * Edit a user.
+ *
+ * @throws DatamodelException
+ * @param int $userId ID of the user to delete
+ * @param string $username New name of user
+ * @param string $email Changed e‑mail-address of user
+ * @param string $password Changed plaintext password of user
+ */
+ public function editUser($userId, $username, $email, $password)
+ {
+ try {
+ // Update user data
+ $this->db->query(
+ 'UPDATE users '.
+ 'SET username = ?, url = ?, email = ? '.
+ 'WHERE id = ?',
+ 'sssi',
+ $username,
+ \nre\core\Linker::createLinkParam($username),
+ $email,
+ $userId
+ );
+
+ // Set new password
+ if(!empty($password))
+ {
+ $this->db->query(
+ 'UPDATE users '.
+ 'SET password = ? '.
+ 'WHERE id = ?',
+ 'si',
+ $this->hash($password),
+ $userId
+ );
+ }
+ }
+ catch(Exception $e) {
+ $this->db->rollback();
+ $this->db->setAutocommit(true);
+ throw $e;
+ }
+ $this->db->setAutocommit(true);
+ }
+
+
+ /**
+ * Delete a user.
+ *
+ * @param int $userId ID of the user to delete
+ */
+ public function deleteUser($userId)
+ {
+ $this->db->query('DELETE FROM users WHERE id = ?', 'i', $userId);
+ }
+
+
+
+
+ /**
+ * Hash a password.
+ *
+ * @param string $password Plaintext password
+ * @return string Hashed password
+ */
+ public function hash($password)
+ {
+ if(!function_exists('password_hash')) {
+ \hhu\z\lib\Password::load();
+ }
+
+
+ return password_hash($password, PASSWORD_DEFAULT);
+ }
+
+
+ /**
+ * Verify a password.
+ *
+ * @param string $password Plaintext password to verify
+ * @param string $hash Hashed password to match with
+ * @return boolean Verified
+ */
+ private function verify($password, $hash)
+ {
+ if(!function_exists('password_verify')) {
+ \hhu\z\lib\Password::load();
+ }
+
+
+ return password_verify($password, $hash);
+ }
+
+ }
+
+?>
diff --git a/models/UserseminaryrolesModel.inc b/models/UserseminaryrolesModel.inc
new file mode 100644
index 00000000..c07d4c7a
--- /dev/null
+++ b/models/UserseminaryrolesModel.inc
@@ -0,0 +1,78 @@
+
+ * @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;
+
+
+ /**
+ * Model to interact with userseminaryroles-table.
+ *
+ * @author Oliver Hanraths
+ */
+ class UserseminaryrolesModel extends \hhu\z\Model
+ {
+
+
+
+
+ /**
+ * Construct a new UserseminaryrolesModel.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+
+
+
+ /**
+ * Get all userseminaryroles for an user referenced by its ID.
+ *
+ * @param int $userId ID of an user
+ * @return array Userseminaryroles for an user
+ */
+ public function getUserseminaryrolesForUserById($userId, $seminaryId)
+ {
+ return $this->db->query(
+ 'SELECT userseminaryroles.id, userseminaryroles.created, userseminaryroles.name '.
+ 'FROM users_userseminaryroles '.
+ 'LEFT JOIN userseminaryroles ON userseminaryroles.id = users_userseminaryroles.userseminaryrole_id '.
+ 'WHERE users_userseminaryroles.user_id = ? AND users_userseminaryroles.seminary_id = ?',
+ 'ii',
+ $userId, $seminaryId
+ );
+ }
+
+
+ /**
+ * Get all userseminaryroles for an user referenced by its
+ * URL-username.
+ *
+ * @param string $userUrl URL-Username of an user
+ * @return array Userseminaryroles for an user
+ */
+ public function getUserrolesForUserByUrl($userUrl)
+ {
+ return $this->db->query(
+ 'SELECT userroles.id, userroles.created, userroles.name '.
+ 'FROM users '.
+ 'LEFT JOIN users_userseminaryroles ON users_userseminaryroles.user_id = users.id '.
+ 'LEFT JOIN userseminaryroles ON userseminaryroles.id = users_userseminaryroles.userseminaryrole_id '.
+ 'WHERE users.url = ?',
+ 's',
+ $userUrl
+ );
+ }
+
+ }
+
+?>
diff --git a/questtypes/dragndrop/DragndropQuesttypeAgent.inc b/questtypes/dragndrop/DragndropQuesttypeAgent.inc
new file mode 100644
index 00000000..c7d0abdf
--- /dev/null
+++ b/questtypes/dragndrop/DragndropQuesttypeAgent.inc
@@ -0,0 +1,24 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * QuesttypeAgent for Drag&Drop.
+ *
+ * @author Oliver Hanraths
+ */
+ class DragndropQuesttypeAgent extends \hhu\z\QuesttypeAgent
+ {
+ }
+
+?>
diff --git a/questtypes/dragndrop/DragndropQuesttypeController.inc b/questtypes/dragndrop/DragndropQuesttypeController.inc
new file mode 100644
index 00000000..fa801be2
--- /dev/null
+++ b/questtypes/dragndrop/DragndropQuesttypeController.inc
@@ -0,0 +1,148 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Controller of the DragndropQuesttypeAgent for Drag&Drop.
+ *
+ * @author Oliver Hanraths
+ */
+ class DragndropQuesttypeController extends \hhu\z\QuesttypeController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('media');
+
+
+
+
+ /**
+ * Save the answers of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Get Drag&Drop field
+ $dndField = $this->Dragndrop->getDragndrop($quest['id']);
+
+ // Get Drops
+ $drops = $this->Dragndrop->getDrops($dndField['quest_id']);
+
+ // Save user answers
+ foreach($drops as &$drop)
+ {
+ if(!array_key_exists($drop['id'], $answers)) {
+ $this->Dragndrop->setCharacterSubmission($drop['id'], $character['id'], $answers[$drop['id']]);
+ }
+ }
+ }
+
+
+ /**
+ * Check if answers of a Character for a Quest match the correct ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @return boolean True/false for a right/wrong answer or null for moderator evaluation
+ */
+ public function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Get Drag&Drop field
+ $dndField = $this->Dragndrop->getDragndrop($quest['id']);
+
+ // Get Drops
+ $drops = $this->Dragndrop->getDrops($dndField['quest_id']);
+
+ // Match drops with user answers
+ $allSolved = true;
+ foreach($drops as &$drop)
+ {
+ if(!array_key_exists($drop['id'], $answers) || $answer[$drop['id']] != $drop['questtypes_dragndrop_drag_id'])
+ {
+ $allSolved = false;
+ break;
+ }
+ }
+
+
+ // Set status
+ return $allSolved;
+ }
+
+
+ /**
+ * Action: quest.
+ *
+ * Display a text with input fields and evaluate if user input
+ * matches with stored regular expressions.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function quest($seminary, $questgroup, $quest, $character)
+ {
+ // Get Drag&Drop field
+ $dndField = $this->Dragndrop->getDragndrop($quest['id']);
+ $dndField['media'] = $this->Media->getMediaById($dndField['questmedia_id']);
+
+ // Get Drops
+ $drops = $this->Dragndrop->getDrops($dndField['quest_id']);
+
+ // Get Drags
+ $drags = $this->Dragndrop->getDrags($dndField['quest_id']);
+ foreach($drags as &$drag) {
+ $drag['media'] = $this->Media->getMediaById($drag['questmedia_id']);
+ }
+
+ // Has Character already solved Quest?
+ $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+
+ // Pass data to view
+ $this->set('seminary', $seminary);
+ $this->set('field', $dndField);
+ $this->set('drops', $drops);
+ $this->set('drags', $drags);
+ }
+
+
+ /**
+ * Action: submission.
+ * TODO submission()
+ *
+ * Show the submission of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function submission($seminary, $questgroup, $quest, $character)
+ {
+ }
+
+ }
+
+?>
diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc
new file mode 100644
index 00000000..676e502f
--- /dev/null
+++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc
@@ -0,0 +1,109 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Model of the DragndropQuesttypeAgent for Drag&Drop.
+ *
+ * @author Oliver Hanraths
+ */
+ class DragndropQuesttypeModel extends \hhu\z\QuesttypeModel
+ {
+
+
+
+
+ /**
+ * Get Drag&Drop-field.
+ *
+ * @param int $questId ID of Quest
+ * @return array Drag&Drop-field
+ */
+ public function getDragndrop($questId)
+ {
+ $data = $this->db->query(
+ 'SELECT quest_id, questmedia_id, width, height '.
+ 'FROM questtypes_dragndrop '.
+ 'WHERE quest_id = ?',
+ 'i',
+ $questId
+ );
+ if(!empty($data)) {
+ return $data[0];
+ }
+
+
+ return null;
+ }
+
+
+ /**
+ * Get Drop-items.
+ *
+ * @param int $dragndropId ID of Drag&Drop-field
+ * @return array Drop-items
+ */
+ public function getDrops($dragndropId)
+ {
+ return $this->db->query(
+ 'SELECT id, top, `left`, width, height, questtypes_dragndrop_drag_id '.
+ 'FROM questtypes_dragndrop_drops '.
+ 'WHERE questtypes_dragndrop_id = ?',
+ 'i',
+ $dragndropId
+ );
+ }
+
+
+ /**
+ * Get Drag-items.
+ *
+ * @param int $dragndropId ID of Drag&Drop-field
+ * @return array Drag-items
+ */
+ public function getDrags($dragndropId)
+ {
+ return $this->db->query(
+ 'SELECT id, questmedia_id '.
+ 'FROM questtypes_dragndrop_drags '.
+ 'WHERE questtypes_dragndrop_id = ?',
+ 'i',
+ $dragndropId
+ );
+ }
+
+
+ /**
+ * Save Character’s submitted answer for one Drag&Drop-field.
+ *
+ * @param int $dropId ID of Drop-field
+ * @param int $characterId ID of Character
+ * @param string $answer Submitted Drag-field-ID for this field
+ */
+ public function setCharacterSubmission($dropId, $characterId, $answer)
+ {
+ $this->db->query(
+ 'INSERT INTO questtypes_dragndrop_drops_characters '.
+ '(questtypes_dragndrop_drops_id, character_id, questtypes_dragndrop_drag_id) '.
+ 'VALUES '.
+ '(?, ?, ?) '.
+ 'ON DUPLICATE KEY UPDATE '.
+ 'questtypes_dragndrop_drag_id = ?',
+ 'iiii',
+ $dropId, $characterId, $answer, $answer
+ );
+ }
+
+ }
+
+?>
diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl
new file mode 100644
index 00000000..74e497c7
--- /dev/null
+++ b/questtypes/dragndrop/html/quest.tpl
@@ -0,0 +1,17 @@
+
diff --git a/questtypes/dragndrop/html/submission.tpl b/questtypes/dragndrop/html/submission.tpl
new file mode 100644
index 00000000..dbc453c3
--- /dev/null
+++ b/questtypes/dragndrop/html/submission.tpl
@@ -0,0 +1,7 @@
+ &$text) : ?>
+ 0) : ?>
+=$regexs[$i-1]['answer']?>
+✓✕
+
+=\hhu\z\Utils::t($text)?>
+
diff --git a/questtypes/dummy/DummyQuesttypeAgent.inc b/questtypes/dummy/DummyQuesttypeAgent.inc
new file mode 100644
index 00000000..f0de5cb1
--- /dev/null
+++ b/questtypes/dummy/DummyQuesttypeAgent.inc
@@ -0,0 +1,24 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Dummy-QuesttypeAgent for testing basic QuesttypeAgent-functionality.
+ *
+ * @author Oliver Hanraths
+ */
+ class DummyQuesttypeAgent extends \hhu\z\QuesttypeAgent
+ {
+ }
+
+?>
diff --git a/questtypes/dummy/DummyQuesttypeController.inc b/questtypes/dummy/DummyQuesttypeController.inc
new file mode 100644
index 00000000..d9ed7aab
--- /dev/null
+++ b/questtypes/dummy/DummyQuesttypeController.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\questtypes;
+
+
+ /**
+ * Controller of the Dummy-QuesttypeAgent for testing basic
+ * QuesttypeAgent-functionality.
+ *
+ * @author Oliver Hanraths
+ */
+ class DummyQuesttypeController extends \hhu\z\QuesttypeController
+ {
+
+
+
+
+ /**
+ * Save the answers of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Do nothing
+ }
+
+
+ /**
+ * Check if answers of a Character for a Quest match the correct ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ * @return boolean True/false for a right/wrong answer or null for moderator evaluation
+ */
+ public function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Set status
+ return true;
+ }
+
+
+ /**
+ * Action: quest.
+ *
+ * Show the task of a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function quest($seminary, $questgroup, $quest, $character)
+ {
+ // Nothing to do
+ }
+
+
+ /**
+ * Action: submission.
+ *
+ * Show the submission of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function submission($seminary, $questgroup, $quest, $character)
+ {
+ // Nothing to do
+ }
+
+ }
+
+?>
diff --git a/questtypes/dummy/DummyQuesttypeModel.inc b/questtypes/dummy/DummyQuesttypeModel.inc
new file mode 100644
index 00000000..c4aadf3e
--- /dev/null
+++ b/questtypes/dummy/DummyQuesttypeModel.inc
@@ -0,0 +1,25 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Model of the Dummy-QuesttypeAgent for testing basic
+ * QuesttypeAgent-functionality.
+ *
+ * @author Oliver Hanraths
+ */
+ class DummyQuesttypeModel extends \hhu\z\QuesttypeModel
+ {
+ }
+
+?>
diff --git a/questtypes/dummy/html/quest.tpl b/questtypes/dummy/html/quest.tpl
new file mode 100644
index 00000000..f482a07b
--- /dev/null
+++ b/questtypes/dummy/html/quest.tpl
@@ -0,0 +1,4 @@
+
diff --git a/questtypes/dummy/html/submission.tpl b/questtypes/dummy/html/submission.tpl
new file mode 100644
index 00000000..e69de29b
diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc
new file mode 100644
index 00000000..2789c5f6
--- /dev/null
+++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc
@@ -0,0 +1,24 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * QuesttypeAgent for multiple choice.
+ *
+ * @author Oliver Hanraths
+ */
+ class MultiplechoiceQuesttypeAgent extends \hhu\z\QuesttypeAgent
+ {
+ }
+
+?>
diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc
new file mode 100644
index 00000000..57f60e82
--- /dev/null
+++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc
@@ -0,0 +1,143 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Controller of the MultiplechoiceQuesttypeAgent multiple choice.
+ *
+ * @author Oliver Hanraths
+ */
+ class MultiplechoiceQuesttypeController extends \hhu\z\QuesttypeController
+ {
+
+
+
+
+ /**
+ * Save the answers of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Get questions
+ $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']);
+
+ // Save answers
+ foreach($questions as &$question)
+ {
+ $answer = (array_key_exists(intval($question['pos'])-1, $answers)) ? true : false;
+ $this->Multiplechoice->setCharacterSubmission($question['id'], $character['id'], $answer);
+ }
+ }
+
+
+ /**
+ * Check if answers of a Character for a Quest match the correct ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @return boolean True/false for a right/wrong answer or null for moderator evaluation
+ */
+ public function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Get right answers
+ $tickQuestions = $this->Multiplechoice->getTickQuestionsOfQuest($quest['id']);
+
+ // Match tick questions with user answers
+ $allSolved = true;
+ foreach($tickQuestions as &$tickQuestion)
+ {
+ $pos = intval($tickQuestion['pos'])-1;
+ if(!array_key_exists($pos, $answers) || $answers[$pos] != 'true')
+ {
+ $allSolved = false;
+ break;
+ }
+ else {
+ unset($answers[$pos]);
+ }
+ }
+
+
+ // Return status
+ return ($allSolved && count($answers) == 0);
+ }
+
+
+ /**
+ * Action: quest.
+ *
+ * Display questions with a checkbox to let the user choose the
+ * right ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function quest($seminary, $questgroup, $quest, $character)
+ {
+ // Get questions
+ $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']);
+
+ // Get user answers
+ if($this->request->getGetParam('show-answer') == 'true')
+ {
+ foreach($questions as &$question) {
+ $question['answer'] = $this->Multiplechoice->getCharacterSubmission($question['id'], $character['id']);
+ }
+ }
+
+ // Has Character already solved Quest?
+ $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+
+ // Pass data to view
+ $this->set('questions', $questions);
+ $this->set('solved', $solved);
+ }
+
+
+ /**
+ * Action: submission.
+ *
+ * Show the submission of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function submission($seminary, $questgroup, $quest, $character)
+ {
+ // Get questions
+ $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']);
+ foreach($questions as &$question) {
+ $question['answer'] = $this->Multiplechoice->getCharacterSubmission($question['id'], $character['id']);
+ }
+
+
+ // Pass data to view
+ $this->set('questions', $questions);
+ }
+
+ }
+
+?>
diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc
new file mode 100644
index 00000000..fef90bcb
--- /dev/null
+++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc
@@ -0,0 +1,111 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Model of the MultiplechoiceQuesttypeAgent for multiple choice.
+ *
+ * @author Oliver Hanraths
+ */
+ class MultiplechoiceQuesttypeModel extends \hhu\z\QuesttypeModel
+ {
+
+
+
+
+ /**
+ * Get all multiple choice questions of a Quest.
+ *
+ * @param int $questId ID of Quest
+ * @return array Multiple choice questions
+ */
+ public function getQuestionsOfQuest($questId)
+ {
+ return $this->db->query(
+ 'SELECT id, pos, question, tick '.
+ 'FROM questtypes_multiplechoice '.
+ 'WHERE quest_id = ?',
+ 'i',
+ $questId
+ );
+ }
+
+
+ /**
+ * Get all multiple choice questions of a Quest that should be
+ * ticked.
+ *
+ * @param int $questId ID of Quest
+ * @return array Multiple choice questions that should be ticked
+ */
+ public function getTickQuestionsOfQuest($questId)
+ {
+ return $this->db->query(
+ 'SELECT id, question, tick, pos '.
+ 'FROM questtypes_multiplechoice '.
+ 'WHERE quest_id = ? AND tick = True',
+ 'i',
+ $questId
+ );
+ }
+
+
+ /**
+ * Save Character’s submitted answer for one option.
+ *
+ * @param int $multipleChoiceId ID of multiple choice option
+ * @param int $characterId ID of Character
+ * @param boolean $answer Submitted answer for this option
+ */
+ public function setCharacterSubmission($multipleChoiceId, $characterId, $answer)
+ {
+ $this->db->query(
+ 'INSERT INTO questtypes_multiplechoice_characters '.
+ '(questtypes_multiplechoice_id, character_id, ticked) '.
+ 'VALUES '.
+ '(?, ?, ?) '.
+ 'ON DUPLICATE KEY UPDATE '.
+ 'ticked = ?',
+ 'iiii',
+ $multipleChoiceId, $characterId, $answer, $answer
+ );
+ }
+
+
+ /**
+ * Get answer of one option submitted by Character.
+ *
+ * @param int $multipleChoiceId ID of multiple choice option
+ * @param int $characterId ID of Character
+ * @return boolean Submitted answer of Character or false
+ */
+ public function getCharacterSubmission($multipleChoiscId, $characterId)
+ {
+ $data = $this->db->query(
+ 'SELECT ticked '.
+ 'FROM questtypes_multiplechoice_characters '.
+ 'WHERE questtypes_multiplechoice_id = ? AND character_id = ? ',
+ 'ii',
+ $multipleChoiscId, $characterId
+ );
+ if(!empty($data)) {
+ return $data[0]['ticked'];
+ }
+
+
+ return false;
+ }
+
+ }
+
+?>
diff --git a/questtypes/multiplechoice/html/quest.tpl b/questtypes/multiplechoice/html/quest.tpl
new file mode 100644
index 00000000..59c1743d
--- /dev/null
+++ b/questtypes/multiplechoice/html/quest.tpl
@@ -0,0 +1,11 @@
+
diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl
new file mode 100644
index 00000000..0459b59b
--- /dev/null
+++ b/questtypes/multiplechoice/html/submission.tpl
@@ -0,0 +1,9 @@
+
+
+ -
+ ☑☐
+ ✓×
+ =$question['question']?>
+
+
+
diff --git a/questtypes/submit/SubmitQuesttypeAgent.inc b/questtypes/submit/SubmitQuesttypeAgent.inc
new file mode 100644
index 00000000..68ca9ae5
--- /dev/null
+++ b/questtypes/submit/SubmitQuesttypeAgent.inc
@@ -0,0 +1,24 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * QuesttypeAgent for submitting.
+ *
+ * @author Oliver Hanraths
+ */
+ class SubmitQuesttypeAgent extends \hhu\z\QuesttypeAgent
+ {
+ }
+
+?>
diff --git a/questtypes/submit/SubmitQuesttypeController.inc b/questtypes/submit/SubmitQuesttypeController.inc
new file mode 100644
index 00000000..32193bb0
--- /dev/null
+++ b/questtypes/submit/SubmitQuesttypeController.inc
@@ -0,0 +1,129 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Controller of the SubmitQuesttypeAgent for a submit task.
+ *
+ * @author Oliver Hanraths
+ */
+ class SubmitQuesttypeController extends \hhu\z\QuesttypeController
+ {
+ /**
+ * Required models
+ *
+ * @var array
+ */
+ public $models = array('quests');
+
+
+
+
+ /**
+ * Save the answers of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Get already submitted answer
+ $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']);
+
+ // Save answer
+ if(is_null($characterSubmission) && array_key_exists(0, $answers)) {
+ $this->Submit->setCharacterSubmission($quest['id'], $character['id'], $answers[0]);
+ }
+ }
+
+
+ /**
+ * Check if answers of a Character for a Quest match the correct ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ * @return boolean True/false for a right/wrong answer or null for moderator evaluation
+ */
+ public function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // A moderator has to evaluate the answer
+ return null;
+ }
+
+
+ /**
+ * Action: quest.
+ *
+ * Display a big textbox to let the user enter a text that has
+ * to be evaluated by a moderator.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function quest($seminary, $questgroup, $quest, $character)
+ {
+ // Answer (Submission)
+ $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']);
+
+ // Wordcount
+ $wordcount = 0;
+ if(!is_null($characterSubmission)) {
+ $wordcount = count(preg_split('/\s+/', $characterSubmission['text']));
+ }
+
+ // Has Character already solved Quest?
+ $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+
+ // Pass data to view
+ $this->set('submission', $characterSubmission);
+ $this->set('wordcount', $wordcount);
+ $this->set('solved', $solved);
+ }
+
+
+ /**
+ * Action: submission.
+ *
+ * Show the submission of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function submission($seminary, $questgroup, $quest, $character)
+ {
+ // Get Character submission
+ $submission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']);
+
+ // Status
+ $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+
+ // Pass data to view
+ $this->set('submission', $submission);
+ $this->set('solved', $solved);
+ }
+
+ }
+
+?>
diff --git a/questtypes/submit/SubmitQuesttypeModel.inc b/questtypes/submit/SubmitQuesttypeModel.inc
new file mode 100644
index 00000000..4f361533
--- /dev/null
+++ b/questtypes/submit/SubmitQuesttypeModel.inc
@@ -0,0 +1,72 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Model of the SubmitQuesttypeAgent for a submit task.
+ *
+ * @author Oliver Hanraths
+ */
+ class SubmitQuesttypeModel extends \hhu\z\QuesttypeModel
+ {
+
+
+
+
+ /**
+ * Save Character’s submitted text.
+ *
+ * @param int $questId ID of Quest
+ * @param int $characterId ID of Character
+ * @param string $text Submitted text
+ */
+ public function setCharacterSubmission($questId, $characterId, $text)
+ {
+ $this->db->query(
+ 'INSERT INTO questtypes_submit_characters '.
+ '(quest_id, character_id, text) '.
+ 'VALUES '.
+ '(?, ?, ?) ',
+ 'iis',
+ $questId, $characterId, $text
+ );
+ }
+
+
+ /**
+ * Get text submitted by Character.
+ *
+ * @param int $questId ID of Quest
+ * @param int $characterId ID of Character
+ * @return array Text submitted by Character or NULL
+ */
+ public function getCharacterSubmission($questId, $characterId)
+ {
+ $data = $this->db->query(
+ 'SELECT created, text '.
+ 'FROM questtypes_submit_characters '.
+ 'WHERE quest_id = ? AND character_id = ?',
+ 'ii',
+ $questId, $characterId
+ );
+ if(!empty($data)) {
+ return $data[0];
+ }
+
+
+ return null;
+ }
+
+ }
+
+?>
diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl
new file mode 100644
index 00000000..b170ef97
--- /dev/null
+++ b/questtypes/submit/html/quest.tpl
@@ -0,0 +1,14 @@
+
diff --git a/questtypes/submit/html/submission.tpl b/questtypes/submit/html/submission.tpl
new file mode 100644
index 00000000..77403dc9
--- /dev/null
+++ b/questtypes/submit/html/submission.tpl
@@ -0,0 +1,11 @@
+
diff --git a/questtypes/textinput/TextinputQuesttypeAgent.inc b/questtypes/textinput/TextinputQuesttypeAgent.inc
new file mode 100644
index 00000000..8144c148
--- /dev/null
+++ b/questtypes/textinput/TextinputQuesttypeAgent.inc
@@ -0,0 +1,24 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * QuesttypeAgent for inserting text.
+ *
+ * @author Oliver Hanraths
+ */
+ class TextinputQuesttypeAgent extends \hhu\z\QuesttypeAgent
+ {
+ }
+
+?>
diff --git a/questtypes/textinput/TextinputQuesttypeController.inc b/questtypes/textinput/TextinputQuesttypeController.inc
new file mode 100644
index 00000000..13e48612
--- /dev/null
+++ b/questtypes/textinput/TextinputQuesttypeController.inc
@@ -0,0 +1,170 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Controller of the TextinputQuesttypeAgent for for inserting text.
+ *
+ * @author Oliver Hanraths
+ */
+ class TextinputQuesttypeController extends \hhu\z\QuesttypeController
+ {
+
+
+
+
+ /**
+ * Save the answers of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @param array $answers Character answers for the Quest
+ */
+ public function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Get regexs
+ $regexs = $this->Textinput->getTextinputRegexs($quest['id']);
+
+ // Save answers
+ foreach($regexs as &$regex)
+ {
+ $pos = intval($regex['number']) - 1;
+ $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : '';
+ $this->Textinput->setCharacterSubmission($regex['id'], $character['id'], $answer);
+ }
+ }
+
+
+ /**
+ * Check if answers of a Character for a Quest match the correct ones.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ * @return boolean True/false for a right/wrong answer or null for moderator evaluation
+ */
+ public function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers)
+ {
+ // Get right answers
+ $regexs = $this->Textinput->getTextinputRegexs($quest['id']);
+
+ // Match regexs with user answers
+ $allSolved = true;
+ foreach($regexs as $i => &$regex)
+ {
+ if(!array_key_exists($i, $answers))
+ {
+ $allSolved = false;
+ break;
+ }
+
+ if(!$this->isMatching($regex['regex'], $answers[$i]))
+ {
+ $allSolved = false;
+ break;
+ }
+ }
+
+
+ // Set status
+ return $allSolved;
+ }
+
+
+ /**
+ * Action: quest.
+ *
+ * Display a text with input fields and evaluate if user input
+ * matches with stored regular expressions.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function quest($seminary, $questgroup, $quest, $character)
+ {
+ // Get Task
+ $task = $this->Textinput->getTextinputQuest($quest['id']);
+
+ // Process text
+ $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' ', -1, PREG_SPLIT_NO_EMPTY);
+
+ // Get Character answers
+ $regexs = null;
+ if($this->request->getGetParam('show-answer') == 'true')
+ {
+ $regexs = $this->Textinput->getTextinputRegexs($quest['id']);
+ foreach($regexs as &$regex) {
+ $regex['answer'] = $this->Textinput->getCharacterSubmission($regex['id'], $character['id']);
+ }
+ }
+
+ // Has Character already solved Quest?
+ $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']);
+
+
+ // Pass data to view
+ $this->set('texts', $textParts);
+ $this->set('regexs', $regexs);
+ }
+
+
+ /**
+ * Action: submission.
+ *
+ * Show the submission of a Character for a Quest.
+ *
+ * @param array $seminary Current Seminary data
+ * @param array $questgroup Current Questgroup data
+ * @param array $quest Current Quest data
+ * @param array $character Current Character data
+ */
+ public function submission($seminary, $questgroup, $quest, $character)
+ {
+ // Get Task
+ $task = $this->Textinput->getTextinputQuest($quest['id']);
+
+ // Process text
+ $textParts = preg_split('/(\$\$)/', $task['text'], -1, PREG_SPLIT_NO_EMPTY);
+
+ // Get Character answers
+ $regexs = $this->Textinput->getTextinputRegexs($quest['id']);
+ foreach($regexs as &$regex) {
+ $regex['answer'] = $this->Textinput->getCharacterSubmission($regex['id'], $character['id']);
+ $regex['right'] = $this->isMatching($regex['regex'], $regex['answer']);
+ }
+
+
+ // Pass data to view
+ $this->set('texts', $textParts);
+ $this->set('regexs', $regexs);
+ }
+
+
+
+
+ private function isMatching($regex, $answer)
+ {
+ $score = preg_match($regex, $answer);
+
+
+ return ($score !== false && $score > 0);
+ }
+
+ }
+
+?>
diff --git a/questtypes/textinput/TextinputQuesttypeModel.inc b/questtypes/textinput/TextinputQuesttypeModel.inc
new file mode 100644
index 00000000..3d00d038
--- /dev/null
+++ b/questtypes/textinput/TextinputQuesttypeModel.inc
@@ -0,0 +1,117 @@
+
+ * @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\questtypes;
+
+
+ /**
+ * Model of the TextinputQuesttypeAgent for inserting text.
+ *
+ * @author Oliver Hanraths
+ */
+ class TextinputQuesttypeModel extends \hhu\z\QuesttypeModel
+ {
+
+
+
+
+ /**
+ * Get textinput-text for a Quest.
+ *
+ * @param int $questId ID of Quest
+ * @return array Textinput-text
+ */
+ public function getTextinputQuest($questId)
+ {
+ $data = $this->db->query(
+ 'SELECT text '.
+ 'FROM questtypes_textinput '.
+ 'WHERE quest_id = ?',
+ 'i',
+ $questId
+ );
+ if(!empty($data)) {
+ return $data[0];
+ }
+
+
+ return null;
+ }
+
+
+ /**
+ * Get regular expressions for a textinput-text.
+ *
+ * @param int $questId ID of Quest
+ * @return array Regexs
+ */
+ public function getTextinputRegexs($questId)
+ {
+ return $this->db->query(
+ 'SELECT id, number, regex '.
+ 'FROM questtypes_textinput_regexs '.
+ 'WHERE questtypes_textinput_quest_id = ? '.
+ 'ORDER BY number ASC',
+ 'i',
+ $questId
+ );
+ }
+
+
+ /**
+ * Save Character’s submitted answer for one textinput field.
+ *
+ * @param int $regexId ID of regex
+ * @param int $characterId ID of Character
+ * @param string $answer Submitted answer for this field
+ */
+ public function setCharacterSubmission($regexId, $characterId, $answer)
+ {
+ $this->db->query(
+ 'INSERT INTO questtypes_textinput_regexs_characters '.
+ '(questtypes_textinput_regex_id, character_id, value) '.
+ 'VALUES '.
+ '(?, ?, ?) '.
+ 'ON DUPLICATE KEY UPDATE '.
+ 'value = ?',
+ 'iiss',
+ $regexId, $characterId, $answer, $answer
+ );
+ }
+
+
+ /**
+ * Get answer of one regex input field submitted by Character.
+ *
+ * @param int $regexId ID of regex
+ * @param int $characterId ID of Character
+ * @return string Submitted answer for this field or empty string
+ */
+ public function getCharacterSubmission($regexId, $characterId)
+ {
+ $data = $this->db->query(
+ 'SELECT value '.
+ 'FROM questtypes_textinput_regexs_characters '.
+ 'WHERE questtypes_textinput_regex_id = ? AND character_id = ? ',
+ 'ii',
+ $regexId, $characterId
+ );
+ if(!empty($data)) {
+ return $data[0]['value'];
+ }
+
+
+ return '';
+ }
+
+ }
+
+?>
diff --git a/questtypes/textinput/html/quest.tpl b/questtypes/textinput/html/quest.tpl
new file mode 100644
index 00000000..53b7bd08
--- /dev/null
+++ b/questtypes/textinput/html/quest.tpl
@@ -0,0 +1,11 @@
+
diff --git a/questtypes/textinput/html/submission.tpl b/questtypes/textinput/html/submission.tpl
new file mode 100644
index 00000000..dbc453c3
--- /dev/null
+++ b/questtypes/textinput/html/submission.tpl
@@ -0,0 +1,7 @@
+ &$text) : ?>
+ 0) : ?>
+=$regexs[$i-1]['answer']?>
+✓✕
+
+=\hhu\z\Utils::t($text)?>
+
diff --git a/requests/WebRequest.inc b/requests/WebRequest.inc
new file mode 100644
index 00000000..2e0f19b9
--- /dev/null
+++ b/requests/WebRequest.inc
@@ -0,0 +1,401 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\requests;
+
+
+ /**
+ * Representation of a web-request.
+ *
+ * @author coderkun
+ */
+ class WebRequest extends \nre\core\Request
+ {
+ /**
+ * Passed GET-parameters
+ *
+ * @var array
+ */
+ private $getParams = array();
+ /**
+ * Passed POST-parameters
+ *
+ * @var array
+ */
+ private $postParams = array();
+ /**
+ * Stored routes
+ *
+ * @var array
+ */
+ private $routes = array();
+ /**
+ * Stored reverse-routes
+ *
+ * @var array
+ */
+ private $reverseRoutes = array();
+
+
+
+
+ /**
+ * Construct a new web-request.
+ */
+ public function __construct()
+ {
+ // Detect current request
+ $this->detectRequest();
+
+ // Load GET-parameters
+ $this->loadGetParams();
+
+ // Load POST-parameters
+ $this->loadPostParams();
+
+ // Detect AJAX
+ $this->detectAJAX();
+ }
+
+
+
+
+ /**
+ * 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)
+ {
+ if($index == 0) {
+ return $this->getGetParam('layout', $defaultIndex);
+ }
+ else {
+ return parent::getParam($index-1, $defaultIndex);
+ }
+ }
+
+
+ /**
+ * Get all parameters from index on.
+ *
+ * @param int $offset Offset-index
+ * @return array Parameters
+ */
+ public function getParams($offset=0)
+ {
+ if($offset == 0)
+ {
+ return array_merge(
+ array(
+ $this->getGetParam('layout', 'toplevel')
+ ),
+ parent::getParams()
+ );
+ }
+
+
+ return array_slice($this->params, $offset-1);
+ }
+
+
+ /**
+ * Get a GET-parameter.
+ *
+ * @param string $key Key of GET-parameter
+ * @param string $defaultIndex Index of default configuration value for this parameter
+ * @return string Value of GET-parameter
+ */
+ public function getGetParam($key, $defaultIndex=null)
+ {
+ // Check key
+ if(array_key_exists($key, $this->getParams))
+ {
+ // Return value
+ return $this->getParams[$key];
+ }
+
+
+ // Return default value
+ return \nre\core\Config::getDefault($defaultIndex);
+ }
+
+
+ /**
+ * Get all GET-parameters.
+ *
+ * @return array GET-Parameters
+ */
+ public function getGetParams()
+ {
+ return $this->getParams;
+ }
+
+
+ /**
+ * Get a POST-parameter.
+ *
+ * @param string $key Key of POST-parameter
+ * @param string $defaultValue Default value for this parameter
+ * @return string Value of POST-parameter
+ */
+ public function getPostParam($key, $defaultValue=null)
+ {
+ // Check key
+ if(array_key_exists($key, $this->postParams))
+ {
+ // Return value
+ return $this->postParams[$key];
+ }
+
+
+ // Return default value
+ return $defaultValue;
+ }
+
+
+ /**
+ * Get all POST-parameters.
+ *
+ * @return array POST-parameters
+ */
+ public function getPostParams()
+ {
+ return $this->postParams;
+ }
+
+
+ /**
+ * Get the method of the current request.
+ *
+ * @return string Current request method
+ */
+ public function getRequestMethod()
+ {
+ return $_SERVER['REQUEST_METHOD'];
+ }
+
+
+ /**
+ * Add a URL-route.
+ *
+ * @param string $pattern Regex-Pattern that defines the routing
+ * @param string $replacement Regex-Pattern for replacement
+ * @param bool $isLast Stop after that rule
+ */
+ public function addRoute($pattern, $replacement, $isLast=false)
+ {
+ // Store route
+ $this->routes[] = $this->newRoute($pattern, $replacement, $isLast);
+ }
+
+
+ /**
+ * Add a reverse URL-route.
+ *
+ * @param string $pattern Regex-Pattern that defines the reverse routing
+ * @param string $replacement Regex-Pattern for replacement
+ * @param bool $isLast Stop after that rule
+ */
+ public function addReverseRoute($pattern, $replacement, $isLast=false)
+ {
+ // Store reverse route
+ $this->reverseRoutes[] = $this->newRoute($pattern, $replacement, $isLast);
+ }
+
+
+ /**
+ * Apply stored reverse-routes to an URL
+ *
+ * @param string $url URL to apply reverse-routes to
+ * @return string Reverse-routed URL
+ */
+ public function applyReverseRoutes($url)
+ {
+ return $this->applyRoutes($url, $this->reverseRoutes);
+ }
+
+
+ /**
+ * Revalidate the current request
+ */
+ public function revalidate()
+ {
+ $this->detectRequest();
+ }
+
+
+ /**
+ * Get a SERVER-parameter.
+ *
+ * @param string $key Key of SERVER-parameter
+ * @return string Value of SERVER-parameter
+ */
+ public function getServerParam($key)
+ {
+ if(array_key_exists($key, $_SERVER)) {
+ return $_SERVER[$key];
+ }
+
+
+ return null;
+ }
+
+
+
+
+ /**
+ * Detect the current HTTP-request.
+ */
+ private function detectRequest()
+ {
+ // URL ermitteln
+ $url = isset($_GET) && array_key_exists('url', $_GET) ? $_GET['url'] : '';
+ $url = trim($url, '/');
+
+ // Routes anwenden
+ $url = $this->applyRoutes($url, $this->routes);
+
+ // URL splitten
+ $params = explode('/', $url);
+ if(empty($params[0])) {
+ $params = array();
+ }
+
+
+ // Parameter speichern
+ $this->params = $params;
+ }
+
+
+ /**
+ * Determine parameters passed by GET.
+ */
+ private function loadGetParams()
+ {
+ if(isset($_GET)) {
+ $this->getParams = $_GET;
+ }
+ }
+
+
+ /**
+ * Determine parameters passed by POST.
+ */
+ private function loadPostParams()
+ {
+ if(isset($_POST)) {
+ $this->postParams = $_POST;
+ }
+ }
+
+
+ /**
+ * Detect an AJAX-request by checking the X-Requested-With
+ * header and set the layout to 'ajax' in this case.
+ */
+ private function detectAjax()
+ {
+ // Get request headers
+ $headers = apache_request_headers();
+
+ // Check X-Requested-With header and set layout
+ if(array_key_exists('X-Requested-With', $headers) && $headers['X-Requested-With'] == 'XMLHttpRequest') {
+ if(!array_key_exists('layout', $this->getParams)) {
+ $this->getParams['layout'] = 'ajax';
+ }
+ }
+ }
+
+
+ /**
+ * Create a new URL-route.
+ *
+ * @param string $pattern Regex-Pattern that defines the reverse routing
+ * @param string $replacement Regex-Pattern for replacement
+ * @param bool $isLast Stop after that rule
+ * @return array New URL-route
+ */
+ private function newRoute($pattern, $replacement, $isLast=false)
+ {
+ return array(
+ 'pattern' => $pattern,
+ 'replacement' => $replacement,
+ 'isLast' => $isLast
+ );
+ }
+
+
+ /**
+ * Apply given routes to an URL
+ *
+ * @param string $url URL to apply routes to
+ * @param array $routes Routes to apply
+ * @return string Routed URL
+ */
+ private function applyRoutes($url, $routes)
+ {
+ // Traverse given routes
+ foreach($routes as &$route)
+ {
+ // Create and apply Regex
+ $urlR = preg_replace(
+ '>'.$route['pattern'].'>i',
+ $route['replacement'],
+ $url
+ );
+
+ // Split URL
+ $get = '';
+ if(($gpos = strrpos($urlR, '?')) !== false) {
+ $get = substr($urlR, $gpos+1);
+ $urlR = substr($urlR, 0, $gpos);
+ }
+
+ // Has current route changed anything?
+ if($urlR != $url || !empty($get))
+ {
+ // Extract GET-parameters
+ if(strlen($get) > 0)
+ {
+ $gets = explode('&', $get);
+ foreach($gets as $get)
+ {
+ $get = explode('=', $get);
+ if(!array_key_exists($get[0], $this->getParams)) {
+ $this->getParams[$get[0]] = $get[1];
+ }
+ }
+ }
+
+
+ // Stop when route “isLast”
+ if($route['isLast']) {
+ $url = $urlR;
+ break;
+ }
+ }
+
+
+ // Set new URL
+ $url = $urlR;
+ }
+
+
+ // Return routed URL
+ return $url;
+ }
+
+ }
+
+?>
diff --git a/responses/WebResponse.inc b/responses/WebResponse.inc
new file mode 100644
index 00000000..2ca25079
--- /dev/null
+++ b/responses/WebResponse.inc
@@ -0,0 +1,250 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ namespace nre\responses;
+
+
+ /**
+ * Representation of a web-response.
+ *
+ * @author coderkun
+ */
+ class WebResponse extends \nre\core\Response
+ {
+ /**
+ * Applied GET-parameters
+ *
+ * @var array
+ */
+ private $getParams = array();
+ /**
+ * Changed header lines
+ *
+ * @var array
+ */
+ private $headers = array();
+
+
+
+
+ /**
+ * Add a parameter.
+ *
+ * @param mixed $value Value of parameter
+ */
+ public function addParam($value)
+ {
+ if(array_key_exists('layout', $this->getParams)) {
+ parent::addParam($value);
+ }
+ else {
+ $this->addGetParam('layout', $value);
+ }
+ }
+
+
+ /**
+ * Add multiple parameters.
+ *
+ * @param mixed $value1 Value of first parameter
+ * @param mixed … Values of further parameters
+ */
+ public function addParams($value1)
+ {
+ $this->addParam($value1);
+
+ $this->params = array_merge(
+ $this->params,
+ array_slice(
+ func_get_args(),
+ 1
+ )
+ );
+ }
+
+
+ /**
+ * Delete all stored parameters (from offset on).
+ *
+ * @param int $offset Offset-index
+ */
+ public function clearParams($offset=0)
+ {
+ if($offset == 0) {
+ unset($this->getParams['layout']);
+ }
+
+ parent::clearParams(max(0, $offset-1));
+ }
+
+
+ /**
+ * 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)
+ {
+ if($index == 0) {
+ return $this->getGetParam('layout', $defaultIndex);
+ }
+ else {
+ return parent::getParam($index-1, $defaultIndex);
+ }
+ }
+
+
+ /**
+ * Get all parameters from index on.
+ *
+ * @param int $offset Offset-index
+ * @return array Parameter values
+ */
+ public function getParams($offset=0)
+ {
+ if($offset == 0)
+ {
+ if(!array_key_exists('layout', $this->getParams)) {
+ return array();
+ }
+
+ return array_merge(
+ array(
+ $this->getParams['layout']
+ ),
+ $this->params
+ );
+ }
+
+
+ return array_slice($this->params, $offset-1);
+ }
+
+
+ /**
+ * Add a GET-parameter.
+ *
+ * @param string $key Key of GET-parameter
+ * @param mixed $value Value of GET-parameter
+ */
+ public function addGetParam($key, $value)
+ {
+ $this->getParams[$key] = $value;
+ }
+
+
+ /**
+ * Add multiple GET-parameters.
+ *
+ * @param array $params Associative arary with key-value GET-parameters
+ */
+ public function addGetParams($params)
+ {
+ $this->getParams = array_merge(
+ $this->getParams,
+ $params
+ );
+ }
+
+
+ /**
+ * Get a GET-parameter.
+ *
+ * @param int $index Index of GET-parameter
+ * @param string $defaultIndex Index of default configuration value for this parameter
+ * @return string Value of GET-parameter
+ */
+ public function getGetParam($key, $defaultIndex=null)
+ {
+ // Check key
+ if(array_key_exists($key, $this->getParams))
+ {
+ // Return value
+ return $this->getParams[$key];
+ }
+
+ // Return default value
+ return \nre\core\Config::getDefault($defaultIndex);
+ }
+
+
+ /**
+ * Get all GET-parameters.
+ *
+ * @return array All GET-parameters
+ */
+ public function getGetParams()
+ {
+ return $this->getParams;
+ }
+
+
+ /**
+ * Add a line to the response header.
+ *
+ * @param string $headerLine Header line
+ * @param bool $replace Replace existing header line
+ * @param int $http_response_code HTTP-response code
+ */
+ public function addHeader($headerLine, $replace=true, $http_response_code=null)
+ {
+ $this->headers[] = $this->newHeader($headerLine, $replace, $http_response_code);
+ }
+
+
+ /**
+ * Clear all stored headers.
+ */
+ public function clearHeaders()
+ {
+ $this->headers = array();
+ }
+
+
+ /**
+ * Send stored headers.
+ */
+ public function header()
+ {
+ foreach($this->headers as $header)
+ {
+ header(
+ $header['string'],
+ $header['replace'],
+ $header['responseCode']
+ );
+ }
+ }
+
+
+
+
+ /**
+ * Create a new header line.
+ *
+ * @param string $headerLine Header line
+ * @param bool $replace Replace existing header line
+ * @param int $http_response_code HTTP-response code
+ */
+ private function newHeader($headerLine, $replace=true, $http_response_code=null)
+ {
+ return array(
+ 'string' => $headerLine,
+ 'replace' => $replace,
+ 'responseCode' => $http_response_code
+ );
+ }
+
+ }
+
+?>
diff --git a/tmp/empty b/tmp/empty
new file mode 100644
index 00000000..e69de29b
diff --git a/views/binary/binary.tpl b/views/binary/binary.tpl
new file mode 100644
index 00000000..43a06a51
--- /dev/null
+++ b/views/binary/binary.tpl
@@ -0,0 +1 @@
+=$intermediate?>
diff --git a/views/binary/error/index.tpl b/views/binary/error/index.tpl
new file mode 100644
index 00000000..2d8440b4
--- /dev/null
+++ b/views/binary/error/index.tpl
@@ -0,0 +1 @@
+=_('Error')?>: =$code?>: =$string?>
diff --git a/views/binary/media/index.tpl b/views/binary/media/index.tpl
new file mode 100644
index 00000000..0d6fb0df
--- /dev/null
+++ b/views/binary/media/index.tpl
@@ -0,0 +1 @@
+=$file?>
diff --git a/views/error.tpl b/views/error.tpl
new file mode 100644
index 00000000..f45b01a0
--- /dev/null
+++ b/views/error.tpl
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Service Unavailable
+
+
+
+ Die Anwendung steht zur Zeit leider nicht zur Verfügung.
+
+
+
diff --git a/views/fault/error/index.tpl b/views/fault/error/index.tpl
new file mode 100644
index 00000000..f7e42500
--- /dev/null
+++ b/views/fault/error/index.tpl
@@ -0,0 +1,2 @@
+Fehler
+=$code?>: =$string?>
diff --git a/views/fault/fault.tpl b/views/fault/fault.tpl
new file mode 100644
index 00000000..307b772d
--- /dev/null
+++ b/views/fault/fault.tpl
@@ -0,0 +1,16 @@
+
+
+
+
+
+ The Legend of Z
+
+
+
+ The Legend of Z
+
+ =$intermediate?>
+
+
+
+
diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl
new file mode 100644
index 00000000..a6d65e71
--- /dev/null
+++ b/views/html/charactergroups/group.tpl
@@ -0,0 +1,33 @@
+
+=$seminary['title']?>
+
+
+=$group['name']?>
+
+
+ XPs: =$group['xps']?>
+
+
+
+
+
+ =_('Quests')?>
+
+
+
+
+ | =$dateFormatter->format(new \DateTime($quest['created']))?> |
+ =$quest['title']?> |
+ =$quest['group_xps']?>/=$quest['xps']?> XPs |
+
+
+
+
+
diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl
new file mode 100644
index 00000000..fdae0ed5
--- /dev/null
+++ b/views/html/charactergroups/groupsgroup.tpl
@@ -0,0 +1,18 @@
+
+=$seminary['title']?>
+
+=$groupsgroup['name']?>
+
+
+
+
+=_('Character Groups Quests')?>
+
diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl
new file mode 100644
index 00000000..e1726ce7
--- /dev/null
+++ b/views/html/charactergroups/index.tpl
@@ -0,0 +1,9 @@
+
+=$seminary['title']?>
+=_('Character Groups')?>
+
+
diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl
new file mode 100644
index 00000000..c3424996
--- /dev/null
+++ b/views/html/charactergroupsquests/quest.tpl
@@ -0,0 +1,46 @@
+
+=$seminary['title']?>
+=_('Character Groups Quests')?>
+=$quest['title']?>
+
+
+
+
+
+
+ XPs: =$quest['xps']?>
+ =_('Description')?>
+ =\hhu\z\Utils::t($quest['description'])?>
+
+ =_('Rules')?>
+ =\hhu\z\Utils::t($quest['rules'])?>
+
+
+
+
+
+ =_('Won Quest')?>
+ =\hhu\z\Utils::t($quest['won_text'])?>
+
+
+
+
+ =_('Lost Quest')?>
+ =\hhu\z\Utils::t($quest['lost_text'])?>
+
+
+
+
+ =$groupsgroup['name']?>
+
+
+
+
+ | =$dateFormatter->format(new \DateTime($group['created']))?> |
+ =$group['name']?> |
+ =$group['xps']?>/=$quest['xps']?> XPs |
+
+
+
+
+
diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl
new file mode 100644
index 00000000..8c6f17d3
--- /dev/null
+++ b/views/html/characters/character.tpl
@@ -0,0 +1,25 @@
+
+=_('Characters')?>
+=$character['name']?>
+
+
+
+ XPs: =$character['xps']?> (=_('Level')?> =$character['xplevel']['level']?>: =$character['xplevel']['name']?>)
+ =_('User')?>: =$user['username']?>
+
+ =$field['title']?>: =$field['value']?>
+
+
+
+
+
+
+
+
+ =_('Character Groups')?>
+
+
diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl
new file mode 100644
index 00000000..cd0fde8b
--- /dev/null
+++ b/views/html/characters/index.tpl
@@ -0,0 +1,8 @@
+
+=_('Characters')?>
+
+
+
+ - =$character['name']?> (=$character['xps']?> XPs, =_('Level')?> =$character['xplevel']['level']?>: =$character['xplevel']['name']?>)
+
+
diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl
new file mode 100644
index 00000000..7f2f0cbb
--- /dev/null
+++ b/views/html/error/index.tpl
@@ -0,0 +1,2 @@
+=_('Error')?>
+=$code?>: =$string?>
diff --git a/views/html/html.tpl b/views/html/html.tpl
new file mode 100644
index 00000000..cc7bcb76
--- /dev/null
+++ b/views/html/html.tpl
@@ -0,0 +1,40 @@
+
+
+
+
+
+ The Legend of Z
+
+
+
+
+
+
+
+
+
+
+
+
+
+ =$intermediate?>
+
+
+
+
diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl
new file mode 100644
index 00000000..de8740da
--- /dev/null
+++ b/views/html/introduction/index.tpl
@@ -0,0 +1,21 @@
+=_('Introduction')?>
+Ein Projekt zum Thema „Gamification im Lehrumfeld“ – basierend auf Die Legende von Zyren.
+Entwickler:
+
+ -
+ Oliver Hanraths
+ Programmierung und Datenbank
+
+ -
+ Daniel Miskovic
+ GUI und Webdesign
+
+ -
+ Kathrin Knautz, B.A., M.A.
+ Leitung
+
+
+
+
+ Heinrich-Heine-Universität Düsseldorf
+
diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl
new file mode 100644
index 00000000..470912e8
--- /dev/null
+++ b/views/html/menu/index.tpl
@@ -0,0 +1,14 @@
+
diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl
new file mode 100644
index 00000000..a80ec0ae
--- /dev/null
+++ b/views/html/questgroups/questgroup.tpl
@@ -0,0 +1,56 @@
+
+
+=$questgroupshierarchypath?>
+=$questgroupspicture?>
+
+=$questgroupshierarchy['title_singular']?> =$questgroup['pos']?>: =$questgroup['title']?>
+
+=\hhu\z\Utils::t($text['text'])?>
+
+
+
+ 0) : ?>
+=$hierarchy['title_plural']?>
+
+
+
+
+
+=_('Quests')?>
+
+
diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl
new file mode 100644
index 00000000..c51e0e8f
--- /dev/null
+++ b/views/html/questgroupshierarchypath/index.tpl
@@ -0,0 +1,7 @@
+ 0) : ?>
+
+
diff --git a/views/html/questgroupspicture/index.tpl b/views/html/questgroupspicture/index.tpl
new file mode 100644
index 00000000..d8c7b32c
--- /dev/null
+++ b/views/html/questgroupspicture/index.tpl
@@ -0,0 +1,3 @@
+
+
+
diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl
new file mode 100644
index 00000000..e69de29b
diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl
new file mode 100644
index 00000000..70155c13
--- /dev/null
+++ b/views/html/quests/quest.tpl
@@ -0,0 +1,85 @@
+
+
+=$questgroupshierarchypath?>
+=$questgroupspicture?>
+
+
+=$quest['title']?>
+
+
+=$quest['title']?>
+
+
+
+
+
+
+
+
+
+ =_('solved')?>
+
+ =_('unsolved')?>
+
+ =\hhu\z\Utils::t($queststatustext)?>
+
+
+
+
+
+ =$questtext['type']?>
+
+ =\hhu\z\Utils::t($questtext['text'])?>
+
+
+
+
+
+
+
+ 1) : ?><
+ =$questtext['pos']?>/=$questtext['count']?>
+ >
+
+
+
+
+
+
+ =_('Task')?>
+ =\hhu\z\Utils::t($quest['task'])?>
+ =$task?>
+
+
+
+
+
+
diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl
new file mode 100644
index 00000000..17f986f2
--- /dev/null
+++ b/views/html/quests/submission.tpl
@@ -0,0 +1,13 @@
+
+
+=$questgroupshierarchypath?>
+=$questgroupspicture?>
+
+
+
+
+
+=sprintf(_('Submission of %s'),$character['name'])?>
+
diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl
new file mode 100644
index 00000000..d5cea955
--- /dev/null
+++ b/views/html/quests/submissions.tpl
@@ -0,0 +1,35 @@
+
+
+=$questgroupshierarchypath?>
+=$questgroupspicture?>
+
+
+=$quest['title']?>
+
+
+=$quest['title']?>
+
+
+
+
+
+
+
+ =_('unsolved')?>
+
+
+ =_('solved')?>
+
+
diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl
new file mode 100644
index 00000000..f50d2af0
--- /dev/null
+++ b/views/html/seminaries/create.tpl
@@ -0,0 +1,10 @@
+=_('Seminaries')?>
+=_('New seminary')?>
+
+
diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl
new file mode 100644
index 00000000..b9542006
--- /dev/null
+++ b/views/html/seminaries/delete.tpl
@@ -0,0 +1,8 @@
+=_('Seminaries')?>
+=_('Delete seminary')?>
+
+=sprintf(_('Should the seminary “%s” really be deleted?'), $seminary['title'])?>
+
diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl
new file mode 100644
index 00000000..7905f17d
--- /dev/null
+++ b/views/html/seminaries/edit.tpl
@@ -0,0 +1,10 @@
+=_('Seminaries')?>
+=_('Edit seminary')?>
+
+
diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl
new file mode 100644
index 00000000..bd5c73e4
--- /dev/null
+++ b/views/html/seminaries/index.tpl
@@ -0,0 +1,14 @@
+=_('Seminaries')?>
+
+
+
+ -
+
+
+ =sprintf(_('created by %s on %s'), $seminary['creator']['username'], $dateFormatter->format(new \DateTime($seminary['created'])))?>
+
+
+
+
diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl
new file mode 100644
index 00000000..135254a9
--- /dev/null
+++ b/views/html/seminaries/seminary.tpl
@@ -0,0 +1,46 @@
+
+=_('Description')?>
+=\hhu\z\Utils::t($seminary['description'])?>
+
+
+=$hierarchy['title_plural']?>
+
+
+ -
+
+
+
+
+ =$hierarchy['title_singular']?> =$group['pos']?>:
+
+ =$group['title']?>
+
+
+
+
+
=$group['character_xps']?> / =$group['xps']?> XP
+
+
+ =$group['text']?>
+
+ Auf ins Abenteuer!
+
+ =_('locked')?>
+
+
+
+
+
+
+
+
+
+
+ =sprintf(_('created by %s on %s'), $seminary['creator']['username'], $dateFormatter->format(new \DateTime($seminary['created'])))?>
+
diff --git a/views/html/userroles/user.tpl b/views/html/userroles/user.tpl
new file mode 100644
index 00000000..df8b7a66
--- /dev/null
+++ b/views/html/userroles/user.tpl
@@ -0,0 +1,5 @@
+
diff --git a/views/html/users/create.tpl b/views/html/users/create.tpl
new file mode 100644
index 00000000..b4e22763
--- /dev/null
+++ b/views/html/users/create.tpl
@@ -0,0 +1,14 @@
+=_('Users')?>
+=_('New user')?>
+
+
diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl
new file mode 100644
index 00000000..ea23061f
--- /dev/null
+++ b/views/html/users/delete.tpl
@@ -0,0 +1,8 @@
+=_('Users')?>
+=_('Delete user')?>
+
+=sprintf(_('Should the user “%s” (%s) really be deleted?'), $user['username'], $user['email'])?>
+
diff --git a/views/html/users/edit.tpl b/views/html/users/edit.tpl
new file mode 100644
index 00000000..bb3655c7
--- /dev/null
+++ b/views/html/users/edit.tpl
@@ -0,0 +1,14 @@
+=_('Users')?>
+=_('Edit user')?>
+
+
diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl
new file mode 100644
index 00000000..f747e6a5
--- /dev/null
+++ b/views/html/users/index.tpl
@@ -0,0 +1,14 @@
+=_('Users')?>
+
+
+
+ -
+
+
+ =sprintf(_('registered on %s'), $dateFormatter->format(new \DateTime($user['created'])))?>
+
+
+
+
diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl
new file mode 100644
index 00000000..f6e0171a
--- /dev/null
+++ b/views/html/users/login.tpl
@@ -0,0 +1,12 @@
+=_('Users')?>
+=_('Login')?>
+
+
diff --git a/views/html/users/logout.tpl b/views/html/users/logout.tpl
new file mode 100644
index 00000000..e69de29b
diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl
new file mode 100644
index 00000000..1ba66b0d
--- /dev/null
+++ b/views/html/users/user.tpl
@@ -0,0 +1,19 @@
+=_('Users')?>
+=$user['username']?>
+
+
+ =sprintf(_('registered on %s'), $dateFormatter->format(new \DateTime($user['created'])))?>
+
+
+=_('Characters')?>
+
+
+=_('Roles')?>
+=$userroles?>
diff --git a/views/inlineerror.tpl b/views/inlineerror.tpl
new file mode 100644
index 00000000..87a2b222
--- /dev/null
+++ b/views/inlineerror.tpl
@@ -0,0 +1 @@
+Dieser Teil der Anwendung steht zur Zeit leider nicht zur Verfügung.
diff --git a/www/.htaccess b/www/.htaccess
new file mode 100644
index 00000000..63163a80
--- /dev/null
+++ b/www/.htaccess
@@ -0,0 +1,8 @@
+
+ RewriteEngine On
+
+ RewriteBase /
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ index.php?url=$1 [QSA,L,NE]
+
diff --git a/www/css/desktop.css b/www/css/desktop.css
new file mode 100644
index 00000000..91bc3e38
--- /dev/null
+++ b/www/css/desktop.css
@@ -0,0 +1,124 @@
+@charset "UTF-8";
+
+/** CSS-Reset **/
+
+html{font-family:'Open Sans',sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}
+body{margin:0;background:#f7f5f2;color:#5e5c58;font:100%/1.6 'Open Sans',sans-serif;-webkit-animation:bugfix infinite 1s}
+a:focus{outline:thin dotted}
+a:active,a:hover{outline:0}
+b,strong,.fwb{font-weight:bold}
+dfn,.fsi{font-style:italic}
+img{border:0}
+h1,h2,h3{color:#103a3e}
+h2{font-size:120%}
+h3{font-size:100%}
+ul,ol,nav{padding:0;list-style-type:none}
+p{margin:0 0 16px;padding:0}
+audio,canvas,video{display:inline-block}
+audio:not([controls]){display:none;height:0}
+[hidden]{display:none}
+abbr[title]{border-bottom:.0625em dotted}
+hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
+mark{background:#ff0;color:#000}
+code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}
+pre{white-space:pre-wrap}
+q{quotes:"\201C" "\201D" "\2018" "\2019"}
+small{font-size:80%}
+sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
+sup{top:-.5em}
+sub{bottom:-.25em}
+svg:not(:root){overflow:hidden}
+figure{margin:0}
+fieldset{border:.0625em solid #c0c0c0;margin:0 .125em;padding:.35em .625em .75em}
+legend{border:0;padding:0}
+button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
+button,input{line-height:normal}
+button,select{text-transform:none}
+button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
+button[disabled],html input[disabled]{cursor:default}
+input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
+input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
+input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
+button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
+textarea{overflow:auto;vertical-align:top}
+table{border-collapse:collapse;border-spacing:0}
+
+
+/** Initial **/
+
+@-webkit-keyframes bugfix{from{padding:0}to{padding:0}}
+
+.cf:before,.cf:after{display:table;content:""}
+.cf:after{clear:both}
+.cf{zoom:1}
+.cb{clear:both}
+
+.wrap{margin:0 5%;max-height:999999px;min-height:1px}
+
+a{color:#50a4ab;text-decoration:none}
+.fa{padding:0 10px 0 0}
+.fwb{font-weight:700}
+
+header{background:#0f373c;margin-bottom:1.1875em;position:fixed;width:100%;z-index:99}
+header .fa{color:#5e9499}
+
+header nav{z-index:99;margin:0 5%}
+menu{display:none;opacity:0;position:absolute;margin:0;padding:0 0 15px;background:#0f373c;right:0;left:0}
+menu li{display:block;margin:0 5%}
+menu a,#profile{display:block;padding:15px 0;color:#fff}
+
+#profile{float:right}
+.circlenote{margin-left:15px;background:#bcd75e;border-radius:0.8em;display:inline-block;color:#0f373c;font-weight:bold;line-height:1.6em;text-align:center;width:1.6em;font-size:.8em}
+
+#toggle,.toggle{display:none}
+.toggle{display:inline-block;padding:15px 15px 15px 0;position:relative;cursor:pointer;-webkit-touch-callout:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}
+#toggle:checked ~ menu{display:block;opacity:1}
+#navicon{display:block;color:#fff}
+
+article{padding:70px 0 30px}
+
+.breadcrumbs li{display:block;font-size:.875em}
+.breadcrumbs .fa{padding-right:5px;font-size:.75em}
+
+.questgroups li{margin:0 0 25px 0;border-radius:3px;overflow:hidden}
+.questgroups img{display:block}
+.questgroups section{padding:20px;background:#fff}
+
+.qg li{margin:0 0 25px 0}
+.qgtitle a{background:#5cb6bd;border-radius:3px 3px 0 0;display:block;padding:20px;color:#fff;font-weight:700}
+.qgprogress,.qghidden{margin-bottom:4px;padding:20px 20px 4px 20px;background:#fff;border-radius:0 0 3px 3px}
+
+.qglist li{margin: 0 0 5px 0}
+.qglist .qgtitle a{padding:14px;border-radius:3px}
+.qglist .qgtitle .solved{background:#fff;color:#50a4ab}
+.qglist .qgtitle .solved .fa{color:#bcd75e}
+.qglist .qgtitle .bonus{background:#f5821f}
+
+.xpbar{width:60%;float:left;height:10px;position:relative;background:#eee;border-radius:25px;margin:8px 0 16px}
+.xpbar span{display:block;height:100%;border-radius:20px;background:#bcd75e;position:relative;overflow:hidden}
+.xpnumeric{float:right;color:#869845}
+
+.cta{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px}
+.orange{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0}
+
+.qtextbox{font-size:0.875em;background:#fff;height:200px;padding:0 20px;margin:0;border:20px solid #fff;border-width:20px 0 10px;border-radius:3px 3px 0 0;overflow-y:scroll;-webkit-overflow-scrolling:touch}
+.qtexticon{background:#fff;background:linear-gradient(to bottom, rgba(255,255,255,1) 0%,rgba(247,245,242,1) 100%);padding-bottom:5px;border-radius:0 0 3px 3px;text-align:center}
+
+/** Media Queries **/
+
+@media only screen and (min-width:480px){
+.questgroups li{width:48%;float:left}
+.questgroups li:nth-child(even){float:right}
+}
+
+@media only screen and (min-width:1024px){
+header{position:fixed;left:0;top:0;bottom:0;width:300px;margin:0}
+header nav{margin:0 10%}
+menu{display:block;opacity:1;position:relative}
+menu a{padding:10px 0}
+#profile{float:none;margin:30px 0 15px 12px}
+#toggle,.toggle{display:none}
+.wrap{margin:0 20px 0 340px}
+article{padding-top:20px}
+.breadcrumbs li{display:inline;padding-right:10px}
+}
\ No newline at end of file
diff --git a/www/error403.html b/www/error403.html
new file mode 100644
index 00000000..c8867377
--- /dev/null
+++ b/www/error403.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ The Legend of Z
+
+
+
+ The Legend of Z
+ Access denied.
+
+
+
diff --git a/www/error404.html b/www/error404.html
new file mode 100644
index 00000000..874106f4
--- /dev/null
+++ b/www/error404.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ The Legend of Z
+
+
+
+ The Legend of Z
+ Not found.
+
+
+
diff --git a/www/error500.html b/www/error500.html
new file mode 100644
index 00000000..b7afa5c0
--- /dev/null
+++ b/www/error500.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ The Legend of Z
+
+
+
+ The Legend of Z
+ Internal server error.
+
+
+
diff --git a/www/index.php b/www/index.php
new file mode 100644
index 00000000..46301a31
--- /dev/null
+++ b/www/index.php
@@ -0,0 +1,45 @@
+
+ * @copyright 2013 coderkun (http://www.coderkun.de)
+ * @license http://www.gnu.org/licenses/gpl.html
+ * @link http://www.coderkun.de/projects/nre
+ */
+
+ /**
+ * Define constants
+ */
+ // Directory separator
+ if(!defined('DS')) {
+ define('DS', DIRECTORY_SEPARATOR);
+ }
+ // Root directory
+ if(!defined('ROOT')) {
+ define('ROOT', dirname(dirname(__FILE__)));
+ }
+
+
+ /**
+ * De-/Activate error messages
+ */
+ if($_SERVER['SERVER_ADDR'] == '127.0.0.1' || $_SERVER['SERVER_ADDR'] == '::1') {
+ error_reporting(E_ALL);
+ ini_set('display_errors', 1);
+ ini_set('log_errors', 0);
+ }
+ else {
+ error_reporting(E_ALL);
+ ini_set('display_errors', 0);
+ ini_set('log_errors', 1);
+ }
+
+
+ /**
+ * Run application
+ */
+ require ROOT.DS.'bootstrap.inc';
+
+?>