commit 2cf011e1caf9cb083b45e7d2840f827b529bf53e Author: coderkun Date: Sat Mar 22 22:32:43 2014 +0100 correct input fields for Questtype ?Textinput? when marker is at the begin or end of text 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 00000000..1192a653 Binary files /dev/null and b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo differ 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) : ?> + + + + + 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 @@ +
+
    + &$question) : ?> +
  1. + /> + +
  2. + +
+ +
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 @@ +
    + +
  • + + × + +
  • + +
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 @@ +
+
+ + + 1) : ?> +
+ +
+ + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) + + + +
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 @@ +
+ &$text) : ?> + 0) : ?> + + + + + +

+ +
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) : ?> + + + + + 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 @@ + 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 @@ +: : 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 @@ + 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

+

:

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

+
+ +
+ + + 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 @@ +

+

+

+
+
+ +
+ XPs: +
+ +
+

+
    + +
  • ( XPs) 0) : ?>()
  • + +
+
+ +
+

+ + + + + + + + + + +
format(new \DateTime($quest['created']))?>/ 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 @@ +

+

+

+
+ + + + +
+
    + +
  • + +
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 @@ +

+

+

+ +
    + +
  • + +
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 @@ +

+

+

+
+ + + + + +
+

XPs:

+

+

+ +

+

+ +
+ + +
+

+

+
+ + +
+

+

+
+ + +
+

+ + + + + + + + + + +
format(new \DateTime($group['created']))?>/ 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 @@ +

+

+

+ +
+

+ XPs: ( : )
+ :
+ + :
+ +

+ + <?=$character['avatar_description']?> + +
+ +
+

+
    + +
  • ( XPs)
  • + +
+
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 @@ +

+

+ +
    + +
  • ( XPs, : )
  • + +
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 @@ +

+

:

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 + + + + + + + + + + +
+ +
+ + + + 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 @@ +

+

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 @@ + +
  • +
  • +
  • +
  • Gilden
  • +
  • Achievements
  • +
  • Karte
  • +
  • Bibliothek
  • + +
  • + +
  • + +
    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 @@ +

    + + + + +

    :

    + +

    + + + + 0) : ?> +

    +
      + +
    • + +
      +
      +
      + +
      +

      / XP

      +
      + +
      + +
    • + +
    + + + + +

    + + 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 @@ +

    + + + + + +

    + +

    +

    + + + + + + + +
    + +

    + +

    + +

    +
    + + + +
    +

    +

    + +

    +

    + + +
      + +
    • + + +
    • + + +
    • + +
    + + + 1) : ?>< + / + > + +
    + + + +
    +

    +

    + +
    + + + +
    +

    ()

    +

    + 0) : ?> +
      + + +
    • :
    • + +
    • :
    • + + +
    + + : + + Spiel vorbei + +
    + 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 @@ +

    + + + + + + + + +

    +
    + +
    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 @@ +

    + + + + + +

    + +

    +

    + + + + + + +
    +

    +
      + +
    • + +
    • + +
    + +

    +
      + +
    • + +
    • + +
    +
    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 @@ +

    +

    + +
    +
    + +
    +
    + +
    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 @@ +

    +

    + + +
    + + +
    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 @@ +

    +

    + +
    +
    + +
    +
    + +
    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 @@ +

    + +
      + +
    • +

      +
      + 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 @@ +

    +

    +

    + + +

    + + + + + +

    + 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 @@ +

    +

    + +
    +
    + +
    + +
    + +
    +
    + +
    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 @@ +

    +

    + + +
    + + +
    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 @@ +

    +

    + +
    +
    + +
    + +
    + +
    +
    + +
    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 @@ +

    + +
      + +
    • +

      +
      + 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 @@ +

    +

    + +
    +
    + +
    + +
    +
    + +
    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 @@ +

    +

    + +

    + format(new \DateTime($user['created'])))?> +

    + +

    +
      + +
    • ( XPs, : ) ()
    • + +
    + +

    + 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'; + +?>